Crypto++ 8.9
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
16ANONYMOUS_NAMESPACE_BEGIN
17
18using CryptoPP::word32;
19using CryptoPP::word64;
20using CryptoPP::rotlConstant;
21using CryptoPP::rotrConstant;
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.
30template <class W>
31inline 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.
46template <class W>
47inline 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
61template <class W, unsigned int R>
62inline 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
77template <class W, unsigned int R>
78inline 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
93template <class W, unsigned int R>
94inline 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
113template <class W, unsigned int R>
114inline 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
140template <class W, unsigned int R>
141inline 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
167ANONYMOUS_NAMESPACE_END
168
169///////////////////////////////////////////////////////////
170
171NAMESPACE_BEGIN(CryptoPP)
172
173#if (CRYPTOPP_ARM_NEON_AVAILABLE)
174extern size_t SPECK128_Enc_AdvancedProcessBlocks_NEON(const word64* subKeys, size_t rounds,
175 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
176
177extern size_t SPECK128_Dec_AdvancedProcessBlocks_NEON(const word64* subKeys, size_t rounds,
178 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
179#endif
180
181#if (CRYPTOPP_SSE41_AVAILABLE)
182extern size_t SPECK64_Enc_AdvancedProcessBlocks_SSE41(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_SSE41(const word32* subKeys, size_t rounds,
186 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
187#endif
188
189#if (CRYPTOPP_SSSE3_AVAILABLE)
190extern size_t SPECK128_Enc_AdvancedProcessBlocks_SSSE3(const word64* subKeys, size_t rounds,
191 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
192
193extern size_t SPECK128_Dec_AdvancedProcessBlocks_SSSE3(const word64* subKeys, size_t rounds,
194 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
195#endif
196
197#if (CRYPTOPP_ALTIVEC_AVAILABLE)
198extern size_t SPECK128_Enc_AdvancedProcessBlocks_ALTIVEC(const word64* subKeys, size_t rounds,
199 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
200
201extern size_t SPECK128_Dec_AdvancedProcessBlocks_ALTIVEC(const word64* subKeys, size_t rounds,
202 const byte *inBlocks, const byte *xorBlocks, byte *outBlocks, size_t length, word32 flags);
203#endif
204
205std::string SPECK64::Base::AlgorithmProvider() const
206{
207 return "C++";
208}
209
211{
212 return GetAlignmentOf<word32>();
213}
214
215void SPECK64::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs &params)
216{
217 CRYPTOPP_ASSERT(keyLength == 12 || keyLength == 16);
218 CRYPTOPP_UNUSED(params);
219
220 // Building the key schedule table requires {3,4} words workspace.
221 // Encrypting and decrypting requires 4 words workspace.
222 m_kwords = keyLength/sizeof(word32);
223 m_wspace.New(4U);
224
225 // Do the endian gyrations from the paper and align pointers
226 typedef GetBlock<word32, LittleEndian> KeyBlock;
227 KeyBlock kblk(userKey);
228
229 switch (m_kwords)
230 {
231 case 3:
232 m_rkeys.New((m_rounds = 26));
233 kblk(m_wspace[2])(m_wspace[1])(m_wspace[0]);
234 SPECK_ExpandKey_3W<word32, 26>(m_rkeys, m_wspace);
235 break;
236 case 4:
237 m_rkeys.New((m_rounds = 27));
238 kblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]);
239 SPECK_ExpandKey_4W<word32, 27>(m_rkeys, m_wspace);
240 break;
241 default:
243 }
244}
245
246void SPECK64::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
247{
248 // Do the endian gyrations from the paper and align pointers
249 typedef GetBlock<word32, LittleEndian> InBlock;
250 InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
251
252 switch (m_rounds)
253 {
254 case 26:
255 SPECK_Encrypt<word32, 26>(m_wspace+2, m_wspace+0, m_rkeys);
256 break;
257 case 27:
258 SPECK_Encrypt<word32, 27>(m_wspace+2, m_wspace+0, m_rkeys);
259 break;
260 default:
262 }
263
264 // Do the endian gyrations from the paper and align pointers
265 typedef PutBlock<word32, LittleEndian> OutBlock;
266 OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
267}
268
269void SPECK64::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
270{
271 // Do the endian gyrations from the paper and align pointers
272 typedef GetBlock<word32, LittleEndian> InBlock;
273 InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
274
275 switch (m_rounds)
276 {
277 case 26:
278 SPECK_Decrypt<word32, 26>(m_wspace+2, m_wspace+0, m_rkeys);
279 break;
280 case 27:
281 SPECK_Decrypt<word32, 27>(m_wspace+2, m_wspace+0, m_rkeys);
282 break;
283 default:
285 }
286
287 // Do the endian gyrations from the paper and align pointers
288 typedef PutBlock<word32, LittleEndian> OutBlock;
289 OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
290}
291
292///////////////////////////////////////////////////////////
293
294std::string SPECK128::Base::AlgorithmProvider() const
295{
296#if (CRYPTOPP_SPECK128_ADVANCED_PROCESS_BLOCKS)
297# if (CRYPTOPP_SSSE3_AVAILABLE)
298 if (HasSSSE3())
299 return "SSSE3";
300# endif
301# if (CRYPTOPP_ARM_NEON_AVAILABLE)
302 if (HasNEON())
303 return "NEON";
304# endif
305# if (CRYPTOPP_ALTIVEC_AVAILABLE)
306 if (HasAltivec())
307 return "Altivec";
308# endif
309#endif
310 return "C++";
311}
312
314{
315#if (CRYPTOPP_SPECK128_ADVANCED_PROCESS_BLOCKS)
316# if (CRYPTOPP_SSSE3_AVAILABLE)
317 if (HasSSSE3())
318 return 16; // load __m128i
319# endif
320# if (CRYPTOPP_ARM_NEON_AVAILABLE)
321 if (HasNEON())
322 return 8; // load uint64x2_t
323# endif
324# if (CRYPTOPP_ALTIVEC_AVAILABLE)
325 if (HasAltivec())
326 return 16; // load uint64x2_p
327# endif
328#endif
329 return GetAlignmentOf<word64>();
330}
331
332void SPECK128::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs &params)
333{
334 CRYPTOPP_ASSERT(keyLength == 16 || keyLength == 24 || keyLength == 32);
335 CRYPTOPP_UNUSED(params);
336
337 // Building the key schedule table requires {2,3,4} words workspace.
338 // Encrypting and decrypting requires 4 words workspace.
339 m_kwords = keyLength/sizeof(word64);
340 m_wspace.New(4U);
341
342 // Do the endian gyrations from the paper and align pointers
343 typedef GetBlock<word64, LittleEndian> KeyBlock;
344 KeyBlock kblk(userKey);
345
346 switch (m_kwords)
347 {
348 case 2:
349 m_rkeys.New((m_rounds = 32));
350 kblk(m_wspace[1])(m_wspace[0]);
351 SPECK_ExpandKey_2W<word64, 32>(m_rkeys, m_wspace);
352 break;
353 case 3:
354 m_rkeys.New((m_rounds = 33));
355 kblk(m_wspace[2])(m_wspace[1])(m_wspace[0]);
356 SPECK_ExpandKey_3W<word64, 33>(m_rkeys, m_wspace);
357 break;
358 case 4:
359 m_rkeys.New((m_rounds = 34));
360 kblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]);
361 SPECK_ExpandKey_4W<word64, 34>(m_rkeys, m_wspace);
362 break;
363 default:
365 }
366
367#if CRYPTOPP_SPECK128_ADVANCED_PROCESS_BLOCKS
368
369 // Pre-splat the round keys for Altivec forward transformation
370#if CRYPTOPP_ALTIVEC_AVAILABLE
371 if (IsForwardTransformation() && HasAltivec())
372 {
373 AlignedSecBlock presplat(m_rkeys.size()*2);
374 for (size_t i=0, j=0; i<m_rkeys.size(); i++, j+=2)
375 presplat[j+0] = presplat[j+1] = m_rkeys[i];
376 m_rkeys.swap(presplat);
377 }
378#elif CRYPTOPP_SSSE3_AVAILABLE
379 if (IsForwardTransformation() && HasSSSE3())
380 {
381 AlignedSecBlock presplat(m_rkeys.size()*2);
382 for (size_t i=0, j=0; i<m_rkeys.size(); i++, j+=2)
383 presplat[j+0] = presplat[j+1] = m_rkeys[i];
384 m_rkeys.swap(presplat);
385 }
386#endif
387
388#endif // CRYPTOPP_SPECK128_ADVANCED_PROCESS_BLOCKS
389}
390
391void SPECK128::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
392{
393 // Do the endian gyrations from the paper and align pointers
394 typedef GetBlock<word64, LittleEndian> InBlock;
395 InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
396
397 switch (m_rounds)
398 {
399 case 32:
400 SPECK_Encrypt<word64, 32>(m_wspace+2, m_wspace+0, m_rkeys);
401 break;
402 case 33:
403 SPECK_Encrypt<word64, 33>(m_wspace+2, m_wspace+0, m_rkeys);
404 break;
405 case 34:
406 SPECK_Encrypt<word64, 34>(m_wspace+2, m_wspace+0, m_rkeys);
407 break;
408 default:
410 }
411
412 // Do the endian gyrations from the paper and align pointers
413 typedef PutBlock<word64, LittleEndian> OutBlock;
414 OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
415}
416
417void SPECK128::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
418{
419 // Do the endian gyrations from the paper and align pointers
420 typedef GetBlock<word64, LittleEndian> InBlock;
421 InBlock iblk(inBlock); iblk(m_wspace[1])(m_wspace[0]);
422
423 switch (m_rounds)
424 {
425 case 32:
426 SPECK_Decrypt<word64, 32>(m_wspace+2, m_wspace+0, m_rkeys);
427 break;
428 case 33:
429 SPECK_Decrypt<word64, 33>(m_wspace+2, m_wspace+0, m_rkeys);
430 break;
431 case 34:
432 SPECK_Decrypt<word64, 34>(m_wspace+2, m_wspace+0, m_rkeys);
433 break;
434 default:
436 }
437
438 // Do the endian gyrations from the paper and align pointers
439 typedef PutBlock<word64, LittleEndian> OutBlock;
440 OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[3])(m_wspace[2]);
441}
442
443#if (CRYPTOPP_SPECK128_ADVANCED_PROCESS_BLOCKS)
444size_t SPECK128::Enc::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
445 byte *outBlocks, size_t length, word32 flags) const
446{
447#if (CRYPTOPP_SSSE3_AVAILABLE)
448 if (HasSSSE3())
449 return SPECK128_Enc_AdvancedProcessBlocks_SSSE3(m_rkeys, (size_t)m_rounds,
450 inBlocks, xorBlocks, outBlocks, length, flags);
451#endif
452#if (CRYPTOPP_ARM_NEON_AVAILABLE)
453 if (HasNEON())
454 return SPECK128_Enc_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
455 inBlocks, xorBlocks, outBlocks, length, flags);
456#endif
457#if (CRYPTOPP_ALTIVEC_AVAILABLE)
458 if (HasAltivec())
459 return SPECK128_Enc_AdvancedProcessBlocks_ALTIVEC(m_rkeys, (size_t)m_rounds,
460 inBlocks, xorBlocks, outBlocks, length, flags);
461#endif
462 return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
463}
464
465size_t SPECK128::Dec::AdvancedProcessBlocks(const byte *inBlocks, const byte *xorBlocks,
466 byte *outBlocks, size_t length, word32 flags) const
467{
468#if (CRYPTOPP_SSSE3_AVAILABLE)
469 if (HasSSSE3())
470 return SPECK128_Dec_AdvancedProcessBlocks_SSSE3(m_rkeys, (size_t)m_rounds,
471 inBlocks, xorBlocks, outBlocks, length, flags);
472#endif
473#if (CRYPTOPP_ARM_NEON_AVAILABLE)
474 if (HasNEON())
475 return SPECK128_Dec_AdvancedProcessBlocks_NEON(m_rkeys, (size_t)m_rounds,
476 inBlocks, xorBlocks, outBlocks, length, flags);
477#endif
478#if (CRYPTOPP_ALTIVEC_AVAILABLE)
479 if (HasAltivec())
480 return SPECK128_Dec_AdvancedProcessBlocks_ALTIVEC(m_rkeys, (size_t)m_rounds,
481 inBlocks, xorBlocks, outBlocks, length, flags);
482#endif
483 return BlockTransformation::AdvancedProcessBlocks(inBlocks, xorBlocks, outBlocks, length, flags);
484}
485#endif // CRYPTOPP_SPECK128_ADVANCED_PROCESS_BLOCKS
486
487NAMESPACE_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 speck.cpp:313
unsigned int OptimalDataAlignment() const
Provides input and output data alignment for optimal performance.
Definition speck.cpp:210
Library configuration file.
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.
Crypto++ library namespace.
Precompiled header file.
Classes for the Speck block cipher.
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition trap.h:68