Crypto++ 7.0 Free C++ class library of cryptographic schemes

chacha.cpp
1 // chacha.cpp - written and placed in the public domain by Jeffrey Walton.
2 // Based on Wei Dai's Salsa20, Botan's SSE2 implementation,
3 // and Bernstein's reference ChaCha family implementation at
4 // http://cr.yp.to/chacha.html.
5
6 #include "pch.h"
7 #include "config.h"
8 #include "chacha.h"
9 #include "argnames.h"
10 #include "misc.h"
11 #include "cpu.h"
12
13 NAMESPACE_BEGIN(CryptoPP)
14
15 #if (CRYPTOPP_ARM_NEON_AVAILABLE)
16 extern void ChaCha_OperateKeystream_NEON(const word32 *state, const byte* input, byte *output, unsigned int rounds);
17 #endif
18
19 #if (CRYPTOPP_SSE2_INTRIN_AVAILABLE || CRYPTOPP_SSE2_ASM_AVAILABLE)
20 extern void ChaCha_OperateKeystream_SSE2(const word32 *state, const byte* input, byte *output, unsigned int rounds);
21 #endif
22
23 #if (CRYPTOPP_AVX2_AVAILABLE)
24 extern void ChaCha_OperateKeystream_AVX2(const word32 *state, const byte* input, byte *output, unsigned int rounds);
25 #endif
26
27 #if (CRYPTOPP_POWER7_AVAILABLE)
28 extern void ChaCha_OperateKeystream_POWER7(const word32 *state, const byte* input, byte *output, unsigned int rounds);
29 #elif (CRYPTOPP_ALTIVEC_AVAILABLE)
30 extern void ChaCha_OperateKeystream_ALTIVEC(const word32 *state, const byte* input, byte *output, unsigned int rounds);
31 #endif
32
33 #define CHACHA_QUARTER_ROUND(a,b,c,d) \
34 a += b; d ^= a; d = rotlConstant<16,word32>(d); \
35 c += d; b ^= c; b = rotlConstant<12,word32>(b); \
36 a += b; d ^= a; d = rotlConstant<8,word32>(d); \
37 c += d; b ^= c; b = rotlConstant<7,word32>(b);
38
39 #define CHACHA_OUTPUT(x){\
40 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 0, x0 + m_state[0]);\
41 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 1, x1 + m_state[1]);\
42 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 2, x2 + m_state[2]);\
43 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 3, x3 + m_state[3]);\
44 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 4, x4 + m_state[4]);\
45 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 5, x5 + m_state[5]);\
46 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 6, x6 + m_state[6]);\
47 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 7, x7 + m_state[7]);\
48 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 8, x8 + m_state[8]);\
49 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 9, x9 + m_state[9]);\
50 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 10, x10 + m_state[10]);\
51 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 11, x11 + m_state[11]);\
52 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 12, x12 + m_state[12]);\
53 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 13, x13 + m_state[13]);\
54 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 14, x14 + m_state[14]);\
55 CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 15, x15 + m_state[15]);}
56
57 #if defined(CRYPTOPP_DEBUG) && !defined(CRYPTOPP_DOXYGEN_PROCESSING)
58 void ChaCha_TestInstantiations()
59 {
60 ChaCha::Encryption x;
61 }
62 #endif
63
64 std::string ChaCha_Policy::AlgorithmName() const
65 {
66 return std::string("ChaCha")+IntToString(m_rounds);
67 }
68
69 std::string ChaCha_Policy::AlgorithmProvider() const
70 {
71 #if (CRYPTOPP_AVX2_AVAILABLE)
72 if (HasAVX2())
73 return "AVX2";
74 else
75 #endif
76 #if (CRYPTOPP_SSE2_INTRIN_AVAILABLE || CRYPTOPP_SSE2_ASM_AVAILABLE)
77 if (HasSSE2())
78 return "SSE2";
79 else
80 #endif
81 #if (CRYPTOPP_ARM_NEON_AVAILABLE)
82 if (HasNEON())
83 return "NEON";
84 else
85 #endif
86 #if (CRYPTOPP_POWER7_AVAILABLE)
87 if (HasPower7())
88 return "Power7";
89 else
90 #elif (CRYPTOPP_ALTIVEC_AVAILABLE)
91 if (HasAltivec())
92 return "Altivec";
93 else
94 #endif
95 return "C++";
96 }
97
98 void ChaCha_Policy::CipherSetKey(const NameValuePairs &params, const byte *key, size_t length)
99 {
100 CRYPTOPP_UNUSED(params);
101 CRYPTOPP_ASSERT(length == 16 || length == 32);
102
103 m_rounds = params.GetIntValueWithDefault(Name::Rounds(), 20);
104 if (!(m_rounds == 8 || m_rounds == 12 || m_rounds == 20))
105 throw InvalidRounds(ChaCha::StaticAlgorithmName(), m_rounds);
106
107 // "expand 16-byte k" or "expand 32-byte k"
108 m_state[0] = 0x61707865;
109 m_state[1] = (length == 16) ? 0x3120646e : 0x3320646e;
110 m_state[2] = (length == 16) ? 0x79622d36 : 0x79622d32;
111 m_state[3] = 0x6b206574;
112
113 GetBlock<word32, LittleEndian> get1(key);
114 get1(m_state[4])(m_state[5])(m_state[6])(m_state[7]);
115
116 GetBlock<word32, LittleEndian> get2(key + ((length == 32) ? 16 : 0));
117 get2(m_state[8])(m_state[9])(m_state[10])(m_state[11]);
118 }
119
120 void ChaCha_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length)
121 {
122 CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
123 CRYPTOPP_ASSERT(length==8);
124
125 GetBlock<word32, LittleEndian> get(IV);
126 m_state[12] = m_state[13] = 0;
127 get(m_state[14])(m_state[15]);
128 }
129
130 void ChaCha_Policy::SeekToIteration(lword iterationCount)
131 {
132 m_state[12] = (word32)iterationCount; // low word
133 m_state[13] = (word32)SafeRightShift<32>(iterationCount);
134 }
135
136 unsigned int ChaCha_Policy::GetAlignment() const
137 {
138 #if (CRYPTOPP_AVX2_AVAILABLE)
139 if (HasAVX2())
140 return 16;
141 else
142 #endif
143 #if (CRYPTOPP_SSE2_INTRIN_AVAILABLE || CRYPTOPP_SSE2_ASM_AVAILABLE)
144 if (HasSSE2())
145 return 16;
146 else
147 #endif
148 #if (CRYPTOPP_ALTIVEC_AVAILABLE)
149 if (HasAltivec())
150 return 16;
151 else
152 #endif
153 return GetAlignmentOf<word32>();
154 }
155
156 unsigned int ChaCha_Policy::GetOptimalBlockSize() const
157 {
158 #if (CRYPTOPP_AVX2_AVAILABLE)
159 if (HasAVX2())
160 return 8 * BYTES_PER_ITERATION;
161 else
162 #endif
163 #if (CRYPTOPP_SSE2_INTRIN_AVAILABLE || CRYPTOPP_SSE2_ASM_AVAILABLE)
164 if (HasSSE2())
165 return 4*BYTES_PER_ITERATION;
166 else
167 #endif
168 #if (CRYPTOPP_ARM_NEON_AVAILABLE)
169 if (HasNEON())
170 return 4*BYTES_PER_ITERATION;
171 else
172 #endif
173 #if (CRYPTOPP_ALTIVEC_AVAILABLE)
174 if (HasAltivec())
175 return 4*BYTES_PER_ITERATION;
176 else
177 #endif
178 return BYTES_PER_ITERATION;
179 }
180
181 bool ChaCha_Policy::MultiBlockSafe(unsigned int blocks) const
182 {
183 return 0xffffffff - m_state[12] > blocks;
184 }
185
186 // OperateKeystream always produces a key stream. The key stream is written
187 // to output. Optionally a message may be supplied to xor with the key stream.
188 // The message is input, and output = output ^ input.
189 void ChaCha_Policy::OperateKeystream(KeystreamOperation operation,
190 byte *output, const byte *input, size_t iterationCount)
191 {
192 do
193 {
194 #if (CRYPTOPP_AVX2_AVAILABLE)
195 if (HasAVX2())
196 {
197 while (iterationCount >= 8 && MultiBlockSafe(8))
198 {
199 const bool xorInput = (operation & INPUT_NULL) != INPUT_NULL;
200 ChaCha_OperateKeystream_AVX2(m_state, xorInput ? input : NULLPTR, output, m_rounds);
201
202 // MultiBlockSafe avoids overflow on the counter words
203 m_state[12] += 8;
204 //if (m_state[12] < 8)
205 // m_state[13]++;
206
207 input += (!!xorInput) * 8 * BYTES_PER_ITERATION;
208 output += 8 * BYTES_PER_ITERATION;
209 iterationCount -= 8;
210 }
211 }
212 #endif
213
214 #if (CRYPTOPP_SSE2_INTRIN_AVAILABLE || CRYPTOPP_SSE2_ASM_AVAILABLE)
215 if (HasSSE2())
216 {
217 while (iterationCount >= 4 && MultiBlockSafe(4))
218 {
219 const bool xorInput = (operation & INPUT_NULL) != INPUT_NULL;
220 ChaCha_OperateKeystream_SSE2(m_state, xorInput ? input : NULLPTR, output, m_rounds);
221
222 // MultiBlockSafe avoids overflow on the counter words
223 m_state[12] += 4;
224 //if (m_state[12] < 4)
225 // m_state[13]++;
226
227 input += (!!xorInput)*4*BYTES_PER_ITERATION;
228 output += 4*BYTES_PER_ITERATION;
229 iterationCount -= 4;
230 }
231 }
232 #endif
233
234 #if (CRYPTOPP_ARM_NEON_AVAILABLE)
235 if (HasNEON())
236 {
237 while (iterationCount >= 4 && MultiBlockSafe(4))
238 {
239 const bool xorInput = (operation & INPUT_NULL) != INPUT_NULL;
240 ChaCha_OperateKeystream_NEON(m_state, xorInput ? input : NULLPTR, output, m_rounds);
241
242 // MultiBlockSafe avoids overflow on the counter words
243 m_state[12] += 4;
244 //if (m_state[12] < 4)
245 // m_state[13]++;
246
247 input += (!!xorInput)*4*BYTES_PER_ITERATION;
248 output += 4*BYTES_PER_ITERATION;
249 iterationCount -= 4;
250 }
251 }
252 #endif
253
254 #if (CRYPTOPP_POWER7_AVAILABLE)
255 if (HasPower7())
256 {
257 while (iterationCount >= 4 && MultiBlockSafe(4))
258 {
259 const bool xorInput = (operation & INPUT_NULL) != INPUT_NULL;
260 ChaCha_OperateKeystream_POWER7(m_state, xorInput ? input : NULLPTR, output, m_rounds);
261
262 // MultiBlockSafe avoids overflow on the counter words
263 m_state[12] += 4;
264 //if (m_state[12] < 4)
265 // m_state[13]++;
266
267 input += (!!xorInput)*4*BYTES_PER_ITERATION;
268 output += 4*BYTES_PER_ITERATION;
269 iterationCount -= 4;
270 }
271 }
272 #elif (CRYPTOPP_ALTIVEC_AVAILABLE)
273 if (HasAltivec())
274 {
275 while (iterationCount >= 4 && MultiBlockSafe(4))
276 {
277 const bool xorInput = (operation & INPUT_NULL) != INPUT_NULL;
278 ChaCha_OperateKeystream_ALTIVEC(m_state, xorInput ? input : NULLPTR, output, m_rounds);
279
280 // MultiBlockSafe avoids overflow on the counter words
281 m_state[12] += 4;
282 //if (m_state[12] < 4)
283 // m_state[13]++;
284
285 input += (!!xorInput)*4*BYTES_PER_ITERATION;
286 output += 4*BYTES_PER_ITERATION;
287 iterationCount -= 4;
288 }
289 }
290 #endif
291
292 if (iterationCount)
293 {
294 word32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
295
296 x0 = m_state[0]; x1 = m_state[1]; x2 = m_state[2]; x3 = m_state[3];
297 x4 = m_state[4]; x5 = m_state[5]; x6 = m_state[6]; x7 = m_state[7];
298 x8 = m_state[8]; x9 = m_state[9]; x10 = m_state[10]; x11 = m_state[11];
299 x12 = m_state[12]; x13 = m_state[13]; x14 = m_state[14]; x15 = m_state[15];
300
301 for (int i = static_cast<int>(m_rounds); i > 0; i -= 2)
302 {
303 CHACHA_QUARTER_ROUND(x0, x4, x8, x12);
304 CHACHA_QUARTER_ROUND(x1, x5, x9, x13);
305 CHACHA_QUARTER_ROUND(x2, x6, x10, x14);
306 CHACHA_QUARTER_ROUND(x3, x7, x11, x15);
307
308 CHACHA_QUARTER_ROUND(x0, x5, x10, x15);
309 CHACHA_QUARTER_ROUND(x1, x6, x11, x12);
310 CHACHA_QUARTER_ROUND(x2, x7, x8, x13);
311 CHACHA_QUARTER_ROUND(x3, x4, x9, x14);
312 }
313
314 CRYPTOPP_KEYSTREAM_OUTPUT_SWITCH(CHACHA_OUTPUT, BYTES_PER_ITERATION);
315
316 if (++m_state[12] == 0)
317 m_state[13]++;
318 }
319
320 // We may re-enter a SIMD keystream operation from here.
321 } while (iterationCount--);
322 }
323
324 NAMESPACE_END
ChaCha_Info::StaticAlgorithmName
static const char * StaticAlgorithmName()
The algorithm name.
Definition: chacha.h:33
NameValuePairs::GetIntValueWithDefault
int GetIntValueWithDefault(const char *name, int defaultValue) const
Get a named value with type int, with default.
Definition: cryptlib.h:395
argnames.h
Standard names for retrieving values by name when working with NameValuePairs.
HasAltivec
bool HasAltivec()
Determine if a PowerPC processor has Altivec available.
Definition: cpu.h:614
misc.h
Utility functions for the Crypto++ library.
Name::Rounds
const char * Rounds()
int
Definition: argnames.h:24
HasAVX2
bool HasAVX2()
Determines AVX2 availability.
Definition: cpu.h:225
config.h
Library configuration file.
CRYPTOPP_KEYSTREAM_OUTPUT_SWITCH
#define CRYPTOPP_KEYSTREAM_OUTPUT_SWITCH(x, y)
Helper macro to implement OperateKeystream.
Definition: strciphr.h:259
HasPower7
bool HasPower7()
Determine if a PowerPC processor has Power7 available.
Definition: cpu.h:627
INPUT_NULL
Input buffer is NULL.
Definition: strciphr.h:82
InvalidRounds
Exception thrown when an invalid number of rounds is encountered.
Definition: simple.h:59
pch.h
Precompiled header file.
CRYPTOPP_ASSERT
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:60
cpu.h
Functions for CPU features and intrinsics.
Name::IV
const char * IV()
ConstByteArrayParameter, also accepts const byte * for backwards compatibility.
Definition: argnames.h:21
HasSSE2
bool HasSSE2()
Determines SSE2 availability.
Definition: cpu.h:116
GetBlock
Access a block of memory.
Definition: misc.h:2388
IntToString
std::string IntToString(T value, unsigned int base=10)
Converts a value to a string.
Definition: misc.h:632
KeystreamOperation
KeystreamOperation
Keystream operation flags.
Definition: strciphr.h:88
CryptoPP
Crypto++ library namespace.
SymmetricCipherFinal
SymmetricCipher implementation.
Definition: strciphr.h:664
chacha.h
Classes for ChaCha8, ChaCha12 and ChaCha20 stream ciphers.
HasNEON
bool HasNEON()
Determine if an ARM processor has Advanced SIMD available.
Definition: cpu.h:387
NameValuePairs
Interface for retrieving values given their names.
Definition: cryptlib.h:293