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 // Internal compiler error in GCC 3.3 and below
14 #if defined(__GNUC__) && (__GNUC__ < 4)
15 # undef CRYPTOPP_SSE2_INTRIN_AVAILABLE
16 #endif
17 
18 NAMESPACE_BEGIN(CryptoPP)
19 
20 #if (CRYPTOPP_ARM_NEON_AVAILABLE)
21 extern void ChaCha_OperateKeystream_NEON(const word32 *state, const byte* input, byte *output, unsigned int rounds);
22 #endif
23 
24 #if (CRYPTOPP_AVX2_AVAILABLE)
25 extern void ChaCha_OperateKeystream_AVX2(const word32 *state, const byte* input, byte *output, unsigned int rounds);
26 #endif
27 #if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
28 extern void ChaCha_OperateKeystream_SSE2(const word32 *state, const byte* input, byte *output, unsigned int rounds);
29 #endif
30 
31 #if (CRYPTOPP_ALTIVEC_AVAILABLE)
32 extern void ChaCha_OperateKeystream_ALTIVEC(const word32 *state, const byte* input, byte *output, unsigned int rounds);
33 #endif
34 
35 #if defined(CRYPTOPP_DEBUG) && !defined(CRYPTOPP_DOXYGEN_PROCESSING)
ChaCha_TestInstantiations()36 void ChaCha_TestInstantiations()
37 {
38     ChaCha::Encryption x;
39     ChaChaTLS::Encryption y;
40     XChaCha20::Encryption z;
41 }
42 #endif
43 
44 NAMESPACE_END  // CryptoPP
45 
46 ////////////////////////////// ChaCha Core //////////////////////////////
47 
48 #define CHACHA_QUARTER_ROUND(a,b,c,d) \
49     a += b; d ^= a; d = rotlConstant<16,word32>(d); \
50     c += d; b ^= c; b = rotlConstant<12,word32>(b); \
51     a += b; d ^= a; d = rotlConstant<8,word32>(d); \
52     c += d; b ^= c; b = rotlConstant<7,word32>(b);
53 
54 #define CHACHA_OUTPUT(x){\
55     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 0, x0 + state[0]);\
56     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 1, x1 + state[1]);\
57     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 2, x2 + state[2]);\
58     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 3, x3 + state[3]);\
59     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 4, x4 + state[4]);\
60     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 5, x5 + state[5]);\
61     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 6, x6 + state[6]);\
62     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 7, x7 + state[7]);\
63     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 8, x8 + state[8]);\
64     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 9, x9 + state[9]);\
65     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 10, x10 + state[10]);\
66     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 11, x11 + state[11]);\
67     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 12, x12 + state[12]);\
68     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 13, x13 + state[13]);\
69     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 14, x14 + state[14]);\
70     CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 15, x15 + state[15]);}
71 
72 ANONYMOUS_NAMESPACE_BEGIN
73 
74 // Hacks... Bring in all symbols, and supply
75 // the stuff the templates normally provide.
76 using namespace CryptoPP;
77 typedef word32 WordType;
78 enum {BYTES_PER_ITERATION=64};
79 
80 // MultiBlockSafe detects a condition that can arise in the SIMD
81 // implementations where we overflow one of the 32-bit state words during
82 // addition in an intermediate result. Preconditions for the issue include
83 // a user seeks to around 2^32 blocks (256 GB of data) for ChaCha; or a
84 // user specifies an arbitrarily large initial counter block for ChaChaTLS.
85 // Also see https://github.com/weidai11/cryptopp/issues/732.
MultiBlockSafe(unsigned int ctrLow,unsigned int blocks)86 inline bool MultiBlockSafe(unsigned int ctrLow, unsigned int blocks)
87 {
88     return 0xffffffff - ctrLow > blocks;
89 }
90 
91 // OperateKeystream always produces a key stream. The key stream is written
92 // to output. Optionally a message may be supplied to xor with the key stream.
93 // The message is input, and output = output ^ input.
ChaCha_OperateKeystream(KeystreamOperation operation,word32 state[16],word32 & ctrLow,word32 & ctrHigh,word32 rounds,byte * output,const byte * input,size_t iterationCount)94 void ChaCha_OperateKeystream(KeystreamOperation operation,
95         word32 state[16], word32& ctrLow, word32& ctrHigh, word32 rounds,
96         byte *output, const byte *input, size_t iterationCount)
97 {
98     do
99     {
100 #if (CRYPTOPP_AVX2_AVAILABLE)
101         if (HasAVX2())
102         {
103             while (iterationCount >= 8 && MultiBlockSafe(state[12], 8))
104             {
105                 const bool xorInput = (operation & EnumToInt(INPUT_NULL)) != EnumToInt(INPUT_NULL);
106                 ChaCha_OperateKeystream_AVX2(state, xorInput ? input : NULLPTR, output, rounds);
107 
108                 // MultiBlockSafe avoids overflow on the counter words
109                 state[12] += 8;
110 
111                 input += (!!xorInput) * 8 * BYTES_PER_ITERATION;
112                 output += 8 * BYTES_PER_ITERATION;
113                 iterationCount -= 8;
114             }
115         }
116 #endif
117 
118 #if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
119         if (HasSSE2())
120         {
121             while (iterationCount >= 4 && MultiBlockSafe(state[12], 4))
122             {
123                 const bool xorInput = (operation & EnumToInt(INPUT_NULL)) != EnumToInt(INPUT_NULL);
124                 ChaCha_OperateKeystream_SSE2(state, xorInput ? input : NULLPTR, output, rounds);
125 
126                 // MultiBlockSafe avoids overflow on the counter words
127                 state[12] += 4;
128 
129                 input += (!!xorInput)*4*BYTES_PER_ITERATION;
130                 output += 4*BYTES_PER_ITERATION;
131                 iterationCount -= 4;
132             }
133         }
134 #endif
135 
136 #if (CRYPTOPP_ARM_NEON_AVAILABLE)
137         if (HasNEON())
138         {
139             while (iterationCount >= 4 && MultiBlockSafe(state[12], 4))
140             {
141                 const bool xorInput = (operation & EnumToInt(INPUT_NULL)) != EnumToInt(INPUT_NULL);
142                 ChaCha_OperateKeystream_NEON(state, xorInput ? input : NULLPTR, output, rounds);
143 
144                 // MultiBlockSafe avoids overflow on the counter words
145                 state[12] += 4;
146 
147                 input += (!!xorInput)*4*BYTES_PER_ITERATION;
148                 output += 4*BYTES_PER_ITERATION;
149                 iterationCount -= 4;
150             }
151         }
152 #endif
153 
154 #if (CRYPTOPP_ALTIVEC_AVAILABLE)
155         if (HasAltivec())
156         {
157             while (iterationCount >= 4 && MultiBlockSafe(state[12], 4))
158             {
159                 const bool xorInput = (operation & EnumToInt(INPUT_NULL)) != EnumToInt(INPUT_NULL);
160                 ChaCha_OperateKeystream_ALTIVEC(state, xorInput ? input : NULLPTR, output, rounds);
161 
162                 // MultiBlockSafe avoids overflow on the counter words
163                 state[12] += 4;
164 
165                 input += (!!xorInput)*4*BYTES_PER_ITERATION;
166                 output += 4*BYTES_PER_ITERATION;
167                 iterationCount -= 4;
168             }
169         }
170 #endif
171 
172         if (iterationCount)
173         {
174             word32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
175 
176             x0 = state[0];    x1 = state[1];    x2 = state[2];    x3 = state[3];
177             x4 = state[4];    x5 = state[5];    x6 = state[6];    x7 = state[7];
178             x8 = state[8];    x9 = state[9];    x10 = state[10];  x11 = state[11];
179             x12 = state[12];  x13 = state[13];  x14 = state[14];  x15 = state[15];
180 
181             for (int i = static_cast<int>(rounds); i > 0; i -= 2)
182             {
183                 CHACHA_QUARTER_ROUND(x0, x4,  x8, x12);
184                 CHACHA_QUARTER_ROUND(x1, x5,  x9, x13);
185                 CHACHA_QUARTER_ROUND(x2, x6, x10, x14);
186                 CHACHA_QUARTER_ROUND(x3, x7, x11, x15);
187 
188                 CHACHA_QUARTER_ROUND(x0, x5, x10, x15);
189                 CHACHA_QUARTER_ROUND(x1, x6, x11, x12);
190                 CHACHA_QUARTER_ROUND(x2, x7,  x8, x13);
191                 CHACHA_QUARTER_ROUND(x3, x4,  x9, x14);
192             }
193 
194             CRYPTOPP_KEYSTREAM_OUTPUT_SWITCH(CHACHA_OUTPUT, BYTES_PER_ITERATION);
195 
196             // This is state[12] and state[13] from ChaCha. In the case of
197             // ChaChaTLS ctrHigh is a reference to a discard value.
198             if (++ctrLow == 0)
199                 ctrHigh++;
200         }
201 
202     // We may re-enter a SIMD keystream operation from here.
203     } while (iterationCount--);
204 }
205 
206 // XChaCha key derivation
HChaCha_OperateKeystream(const word32 state[16],word32 output[8])207 void HChaCha_OperateKeystream(const word32 state[16], word32 output[8])
208 {
209     word32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
210 
211     x0 = state[0];    x1 = state[1];    x2 = state[2];    x3 = state[3];
212     x4 = state[4];    x5 = state[5];    x6 = state[6];    x7 = state[7];
213     x8 = state[8];    x9 = state[9];    x10 = state[10];  x11 = state[11];
214     x12 = state[12];  x13 = state[13];  x14 = state[14];  x15 = state[15];
215 
216     for (int i = 20; i > 0; i -= 2)
217     {
218         CHACHA_QUARTER_ROUND(x0, x4,  x8, x12);
219         CHACHA_QUARTER_ROUND(x1, x5,  x9, x13);
220         CHACHA_QUARTER_ROUND(x2, x6, x10, x14);
221         CHACHA_QUARTER_ROUND(x3, x7, x11, x15);
222 
223         CHACHA_QUARTER_ROUND(x0, x5, x10, x15);
224         CHACHA_QUARTER_ROUND(x1, x6, x11, x12);
225         CHACHA_QUARTER_ROUND(x2, x7,  x8, x13);
226         CHACHA_QUARTER_ROUND(x3, x4,  x9, x14);
227     }
228 
229     output[0] =  x0; output[1] =  x1;
230     output[2] =  x2; output[3] =  x3;
231     output[4] = x12; output[5] = x13;
232     output[6] = x14; output[7] = x15;
233 }
234 
ChaCha_AlgorithmProvider()235 std::string ChaCha_AlgorithmProvider()
236 {
237 #if (CRYPTOPP_AVX2_AVAILABLE)
238     if (HasAVX2())
239         return "AVX2";
240     else
241 #endif
242 #if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
243     if (HasSSE2())
244         return "SSE2";
245     else
246 #endif
247 #if (CRYPTOPP_ARM_NEON_AVAILABLE)
248     if (HasNEON())
249         return "NEON";
250     else
251 #endif
252 #if (CRYPTOPP_ALTIVEC_AVAILABLE)
253     if (HasAltivec())
254         return "Altivec";
255     else
256 #endif
257     return "C++";
258 }
259 
ChaCha_GetAlignment()260 unsigned int ChaCha_GetAlignment()
261 {
262 #if (CRYPTOPP_AVX2_AVAILABLE)
263     if (HasAVX2())
264         return 16;
265     else
266 #endif
267 #if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
268     if (HasSSE2())
269         return 16;
270     else
271 #endif
272 #if (CRYPTOPP_ALTIVEC_AVAILABLE)
273     if (HasAltivec())
274         return 16;
275     else
276 #endif
277         return GetAlignmentOf<word32>();
278 }
279 
ChaCha_GetOptimalBlockSize()280 unsigned int ChaCha_GetOptimalBlockSize()
281 {
282 #if (CRYPTOPP_AVX2_AVAILABLE)
283     if (HasAVX2())
284         return 8 * BYTES_PER_ITERATION;
285     else
286 #endif
287 #if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
288     if (HasSSE2())
289         return 4*BYTES_PER_ITERATION;
290     else
291 #endif
292 #if (CRYPTOPP_ARM_NEON_AVAILABLE)
293     if (HasNEON())
294         return 4*BYTES_PER_ITERATION;
295     else
296 #endif
297 #if (CRYPTOPP_ALTIVEC_AVAILABLE)
298     if (HasAltivec())
299         return 4*BYTES_PER_ITERATION;
300     else
301 #endif
302         return BYTES_PER_ITERATION;
303 }
304 
305 ANONYMOUS_NAMESPACE_END
306 
NAMESPACE_BEGIN(CryptoPP)307 NAMESPACE_BEGIN(CryptoPP)
308 
309 ////////////////////////////// Bernstein ChaCha //////////////////////////////
310 
311 std::string ChaCha_Policy::AlgorithmName() const
312 {
313     return std::string("ChaCha")+IntToString(m_rounds);
314 }
315 
AlgorithmProvider() const316 std::string ChaCha_Policy::AlgorithmProvider() const
317 {
318     return ChaCha_AlgorithmProvider();
319 }
320 
CipherSetKey(const NameValuePairs & params,const byte * key,size_t length)321 void ChaCha_Policy::CipherSetKey(const NameValuePairs &params, const byte *key, size_t length)
322 {
323     CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 16 || length == 32);
324     CRYPTOPP_UNUSED(key); CRYPTOPP_UNUSED(length);
325 
326     // Use previous rounds as the default value
327     int rounds = params.GetIntValueWithDefault(Name::Rounds(), m_rounds);
328     if (rounds != 20 && rounds != 12 && rounds != 8)
329         throw InvalidRounds(ChaCha::StaticAlgorithmName(), rounds);
330 
331     // Latch a good value
332     m_rounds = rounds;
333 
334     // "expand 16-byte k" or "expand 32-byte k"
335     m_state[0] = 0x61707865;
336     m_state[1] = (length == 16) ? 0x3120646e : 0x3320646e;
337     m_state[2] = (length == 16) ? 0x79622d36 : 0x79622d32;
338     m_state[3] = 0x6b206574;
339 
340     GetBlock<word32, LittleEndian> get1(key);
341     get1(m_state[4])(m_state[5])(m_state[6])(m_state[7]);
342 
343     GetBlock<word32, LittleEndian> get2(key + ((length == 32) ? 16 : 0));
344     get2(m_state[8])(m_state[9])(m_state[10])(m_state[11]);
345 }
346 
CipherResynchronize(byte * keystreamBuffer,const byte * IV,size_t length)347 void ChaCha_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length)
348 {
349     CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
350     CRYPTOPP_ASSERT(length==8); CRYPTOPP_UNUSED(length);
351 
352     GetBlock<word32, LittleEndian> get(IV);
353     m_state[12] = m_state[13] = 0;
354     get(m_state[14])(m_state[15]);
355 }
356 
SeekToIteration(lword iterationCount)357 void ChaCha_Policy::SeekToIteration(lword iterationCount)
358 {
359     m_state[12] = (word32)iterationCount;  // low word
360     m_state[13] = (word32)SafeRightShift<32>(iterationCount);
361 }
362 
GetAlignment() const363 unsigned int ChaCha_Policy::GetAlignment() const
364 {
365     return ChaCha_GetAlignment();
366 }
367 
GetOptimalBlockSize() const368 unsigned int ChaCha_Policy::GetOptimalBlockSize() const
369 {
370     return ChaCha_GetOptimalBlockSize();
371 }
372 
OperateKeystream(KeystreamOperation operation,byte * output,const byte * input,size_t iterationCount)373 void ChaCha_Policy::OperateKeystream(KeystreamOperation operation,
374         byte *output, const byte *input, size_t iterationCount)
375 {
376     ChaCha_OperateKeystream(operation, m_state, m_state[12], m_state[13],
377         m_rounds, output, input, iterationCount);
378 }
379 
380 ////////////////////////////// IETF ChaChaTLS //////////////////////////////
381 
AlgorithmName() const382 std::string ChaChaTLS_Policy::AlgorithmName() const
383 {
384     return std::string("ChaChaTLS");
385 }
386 
AlgorithmProvider() const387 std::string ChaChaTLS_Policy::AlgorithmProvider() const
388 {
389     return ChaCha_AlgorithmProvider();
390 }
391 
CipherSetKey(const NameValuePairs & params,const byte * key,size_t length)392 void ChaChaTLS_Policy::CipherSetKey(const NameValuePairs &params, const byte *key, size_t length)
393 {
394     CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 32);
395     CRYPTOPP_UNUSED(length);
396 
397     // ChaChaTLS is always 20 rounds. Fetch Rounds() to avoid a spurious failure.
398     int rounds = params.GetIntValueWithDefault(Name::Rounds(), ROUNDS);
399     if (rounds != 20)
400         throw InvalidRounds(ChaChaTLS::StaticAlgorithmName(), rounds);
401 
402     // RFC 8439 test vectors use an initial block counter. However, the counter
403     // can be an arbitrary value per RFC 8439 Section 2.4. We stash the counter
404     // away in state[16] and use it for a Resynchronize() operation. I think
405     // the initial counter is used more like a Tweak when non-0, and it should
406     // be provided in Resynchronize() (light-weight re-keying). However,
407     // Resynchronize() does not have an overload that allows us to pass it into
408     // the function, so we have to use the heavier-weight SetKey to change it.
409     word64 block;
410     if (params.GetValue("InitialBlock", block))
411         m_counter = static_cast<word32>(block);
412     else
413         m_counter = 0;
414 
415     // State words are defined in RFC 8439, Section 2.3. Key is 32-bytes.
416     GetBlock<word32, LittleEndian> get(key);
417     get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3])
418         (m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]);
419 }
420 
CipherResynchronize(byte * keystreamBuffer,const byte * IV,size_t length)421 void ChaChaTLS_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length)
422 {
423     CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
424     CRYPTOPP_ASSERT(length==12);
425 
426     // State words are defined in RFC 8439, Section 2.3.
427     m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
428     m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
429 
430     // Copy saved key into state
431     std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32));
432 
433     // State words are defined in RFC 8439, Section 2.3
434     GetBlock<word32, LittleEndian> get(IV);
435     m_state[12] = m_counter;
436     get(m_state[13])(m_state[14])(m_state[15]);
437 }
438 
SeekToIteration(lword iterationCount)439 void ChaChaTLS_Policy::SeekToIteration(lword iterationCount)
440 {
441     // Should we throw here??? If the initial block counter is
442     // large then we can wrap and process more data as long as
443     // data processed in the security context does not exceed
444     // 2^32 blocks or approximately 256 GB of data.
445     CRYPTOPP_ASSERT(iterationCount <= std::numeric_limits<word32>::max());
446     m_state[12] = (word32)iterationCount;  // low word
447 }
448 
GetAlignment() const449 unsigned int ChaChaTLS_Policy::GetAlignment() const
450 {
451     return ChaCha_GetAlignment();
452 }
453 
GetOptimalBlockSize() const454 unsigned int ChaChaTLS_Policy::GetOptimalBlockSize() const
455 {
456     return ChaCha_GetOptimalBlockSize();
457 }
458 
OperateKeystream(KeystreamOperation operation,byte * output,const byte * input,size_t iterationCount)459 void ChaChaTLS_Policy::OperateKeystream(KeystreamOperation operation,
460         byte *output, const byte *input, size_t iterationCount)
461 {
462     word32 discard=0;
463     ChaCha_OperateKeystream(operation, m_state, m_state[12], discard,
464             ROUNDS, output, input, iterationCount);
465 
466     // If this fires it means ChaCha_OperateKeystream generated a counter
467     // block carry that was discarded. The problem is, the RFC does not
468     // specify what should happen when the counter block wraps. All we can
469     // do is inform the user that something bad may happen because we don't
470     // know what we should do.
471     // Also see https://github.com/weidai11/cryptopp/issues/790 and
472     // https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU
473     // CRYPTOPP_ASSERT(discard==0);
474 }
475 
476 ////////////////////////////// IETF XChaCha20 //////////////////////////////
477 
AlgorithmName() const478 std::string XChaCha20_Policy::AlgorithmName() const
479 {
480     return std::string("XChaCha20");
481 }
482 
AlgorithmProvider() const483 std::string XChaCha20_Policy::AlgorithmProvider() const
484 {
485     return ChaCha_AlgorithmProvider();
486 }
487 
CipherSetKey(const NameValuePairs & params,const byte * key,size_t length)488 void XChaCha20_Policy::CipherSetKey(const NameValuePairs &params, const byte *key, size_t length)
489 {
490     CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 32);
491     CRYPTOPP_UNUSED(length);
492 
493     // Use previous rounds as the default value
494     int rounds = params.GetIntValueWithDefault(Name::Rounds(), m_rounds);
495     if (rounds != 20 && rounds != 12)
496         throw InvalidRounds(ChaCha::StaticAlgorithmName(), rounds);
497 
498     // Latch a good value
499     m_rounds = rounds;
500 
501     word64 block;
502     if (params.GetValue("InitialBlock", block))
503         m_counter = static_cast<word32>(block);
504     else
505         m_counter = 1;
506 
507     // Stash key away for use in CipherResynchronize
508     GetBlock<word32, LittleEndian> get(key);
509     get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3])
510         (m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]);
511 }
512 
CipherResynchronize(byte * keystreamBuffer,const byte * iv,size_t length)513 void XChaCha20_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length)
514 {
515     CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
516     CRYPTOPP_ASSERT(length==24);
517 
518     // HChaCha derivation
519     m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
520     m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
521 
522     // Copy saved key into state
523     std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32));
524 
525     GetBlock<word32, LittleEndian> get(iv);
526     get(m_state[12])(m_state[13])(m_state[14])(m_state[15]);
527 
528     // Operate the keystream without adding state back in.
529     // This function also gathers the key words into a
530     // contiguous 8-word block.
531     HChaCha_OperateKeystream(m_state, m_state+4);
532 
533     // XChaCha state
534     m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
535     m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
536 
537     // Setup new IV
538     m_state[12] = m_counter;
539     m_state[13] = 0;
540     m_state[14] = GetWord<word32>(false, LITTLE_ENDIAN_ORDER, iv+16);
541     m_state[15] = GetWord<word32>(false, LITTLE_ENDIAN_ORDER, iv+20);
542 }
543 
SeekToIteration(lword iterationCount)544 void XChaCha20_Policy::SeekToIteration(lword iterationCount)
545 {
546     // Should we throw here??? XChaCha does not have a block
547     // counter, so I'm not sure how to seek on it.
548     CRYPTOPP_ASSERT(0); CRYPTOPP_UNUSED(iterationCount);
549 }
550 
GetAlignment() const551 unsigned int XChaCha20_Policy::GetAlignment() const
552 {
553     return ChaCha_GetAlignment();
554 }
555 
GetOptimalBlockSize() const556 unsigned int XChaCha20_Policy::GetOptimalBlockSize() const
557 {
558     return ChaCha_GetOptimalBlockSize();
559 }
560 
OperateKeystream(KeystreamOperation operation,byte * output,const byte * input,size_t iterationCount)561 void XChaCha20_Policy::OperateKeystream(KeystreamOperation operation,
562         byte *output, const byte *input, size_t iterationCount)
563 {
564     ChaCha_OperateKeystream(operation, m_state, m_state[12], m_state[13],
565             m_rounds, output, input, iterationCount);
566 }
567 
568 NAMESPACE_END
569