Coverage Report

Created: 2024-11-21 07:03

/src/cryptopp/chacha.cpp
Line
Count
Source (jump to first uncovered line)
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)
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
3.92k
    a += b; d ^= a; d = rotlConstant<16,word32>(d); \
50
3.92k
    c += d; b ^= c; b = rotlConstant<12,word32>(b); \
51
3.92k
    a += b; d ^= a; d = rotlConstant<8,word32>(d); \
52
3.92k
    c += d; b ^= c; b = rotlConstant<7,word32>(b);
53
54
49
#define CHACHA_OUTPUT(x){\
55
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 0, x0 + state[0]);\
56
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 1, x1 + state[1]);\
57
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 2, x2 + state[2]);\
58
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 3, x3 + state[3]);\
59
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 4, x4 + state[4]);\
60
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 5, x5 + state[5]);\
61
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 6, x6 + state[6]);\
62
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 7, x7 + state[7]);\
63
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 8, x8 + state[8]);\
64
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 9, x9 + state[9]);\
65
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 10, x10 + state[10]);\
66
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 11, x11 + state[11]);\
67
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 12, x12 + state[12]);\
68
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 13, x13 + state[13]);\
69
49
    CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 14, x14 + state[14]);\
70
49
    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.
86
inline bool MultiBlockSafe(unsigned int ctrLow, unsigned int blocks)
87
0
{
88
0
    return 0xffffffff - ctrLow > blocks;
89
0
}
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.
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
49
{
98
49
    do
99
98
    {
100
98
#if (CRYPTOPP_AVX2_AVAILABLE)
101
98
        if (HasAVX2())
102
98
        {
103
98
            while (iterationCount >= 8 && MultiBlockSafe(state[12], 8))
104
0
            {
105
0
                const bool xorInput = (operation & EnumToInt(INPUT_NULL)) != EnumToInt(INPUT_NULL);
106
0
                ChaCha_OperateKeystream_AVX2(state, xorInput ? input : NULLPTR, output, rounds);
107
108
                // MultiBlockSafe avoids overflow on the counter words
109
0
                state[12] += 8;
110
111
0
                input += (!!xorInput) * 8 * BYTES_PER_ITERATION;
112
0
                output += 8 * BYTES_PER_ITERATION;
113
0
                iterationCount -= 8;
114
0
            }
115
98
        }
116
98
#endif
117
118
98
#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
119
98
        if (HasSSE2())
120
98
        {
121
98
            while (iterationCount >= 4 && MultiBlockSafe(state[12], 4))
122
0
            {
123
0
                const bool xorInput = (operation & EnumToInt(INPUT_NULL)) != EnumToInt(INPUT_NULL);
124
0
                ChaCha_OperateKeystream_SSE2(state, xorInput ? input : NULLPTR, output, rounds);
125
126
                // MultiBlockSafe avoids overflow on the counter words
127
0
                state[12] += 4;
128
129
0
                input += (!!xorInput)*4*BYTES_PER_ITERATION;
130
0
                output += 4*BYTES_PER_ITERATION;
131
0
                iterationCount -= 4;
132
0
            }
133
98
        }
134
98
#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
98
        if (iterationCount)
173
49
        {
174
49
            word32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
175
176
49
            x0 = state[0];    x1 = state[1];    x2 = state[2];    x3 = state[3];
177
49
            x4 = state[4];    x5 = state[5];    x6 = state[6];    x7 = state[7];
178
49
            x8 = state[8];    x9 = state[9];    x10 = state[10];  x11 = state[11];
179
49
            x12 = state[12];  x13 = state[13];  x14 = state[14];  x15 = state[15];
180
181
539
            for (int i = static_cast<int>(rounds); i > 0; i -= 2)
182
490
            {
183
490
                CHACHA_QUARTER_ROUND(x0, x4,  x8, x12);
184
490
                CHACHA_QUARTER_ROUND(x1, x5,  x9, x13);
185
490
                CHACHA_QUARTER_ROUND(x2, x6, x10, x14);
186
490
                CHACHA_QUARTER_ROUND(x3, x7, x11, x15);
187
188
490
                CHACHA_QUARTER_ROUND(x0, x5, x10, x15);
189
490
                CHACHA_QUARTER_ROUND(x1, x6, x11, x12);
190
490
                CHACHA_QUARTER_ROUND(x2, x7,  x8, x13);
191
490
                CHACHA_QUARTER_ROUND(x3, x4,  x9, x14);
192
490
            }
193
194
49
            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
49
            if (++ctrLow == 0)
199
0
                ctrHigh++;
200
49
        }
201
202
    // We may re-enter a SIMD keystream operation from here.
203
98
    } while (iterationCount--);
204
49
}
205
206
// XChaCha key derivation
207
void HChaCha_OperateKeystream(const word32 state[16], word32 output[8])
208
0
{
209
0
    word32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
210
211
0
    x0 = state[0];    x1 = state[1];    x2 = state[2];    x3 = state[3];
212
0
    x4 = state[4];    x5 = state[5];    x6 = state[6];    x7 = state[7];
213
0
    x8 = state[8];    x9 = state[9];    x10 = state[10];  x11 = state[11];
214
0
    x12 = state[12];  x13 = state[13];  x14 = state[14];  x15 = state[15];
215
216
0
    for (int i = 20; i > 0; i -= 2)
217
0
    {
218
0
        CHACHA_QUARTER_ROUND(x0, x4,  x8, x12);
219
0
        CHACHA_QUARTER_ROUND(x1, x5,  x9, x13);
220
0
        CHACHA_QUARTER_ROUND(x2, x6, x10, x14);
221
0
        CHACHA_QUARTER_ROUND(x3, x7, x11, x15);
222
223
0
        CHACHA_QUARTER_ROUND(x0, x5, x10, x15);
224
0
        CHACHA_QUARTER_ROUND(x1, x6, x11, x12);
225
0
        CHACHA_QUARTER_ROUND(x2, x7,  x8, x13);
226
0
        CHACHA_QUARTER_ROUND(x3, x4,  x9, x14);
227
0
    }
228
229
0
    output[0] =  x0; output[1] =  x1;
230
0
    output[2] =  x2; output[3] =  x3;
231
0
    output[4] = x12; output[5] = x13;
232
0
    output[6] = x14; output[7] = x15;
233
0
}
234
235
std::string ChaCha_AlgorithmProvider()
236
0
{
237
0
#if (CRYPTOPP_AVX2_AVAILABLE)
238
0
    if (HasAVX2())
239
0
        return "AVX2";
240
0
    else
241
0
#endif
242
0
#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
243
0
    if (HasSSE2())
244
0
        return "SSE2";
245
0
    else
246
0
#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
0
    return "C++";
258
0
}
259
260
unsigned int ChaCha_GetAlignment()
261
98
{
262
98
#if (CRYPTOPP_AVX2_AVAILABLE)
263
98
    if (HasAVX2())
264
98
        return 16;
265
0
    else
266
0
#endif
267
0
#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
268
0
    if (HasSSE2())
269
0
        return 16;
270
0
    else
271
0
#endif
272
#if (CRYPTOPP_ALTIVEC_AVAILABLE)
273
    if (HasAltivec())
274
        return 16;
275
    else
276
#endif
277
0
        return GetAlignmentOf<word32>();
278
98
}
279
280
unsigned int ChaCha_GetOptimalBlockSize()
281
0
{
282
0
#if (CRYPTOPP_AVX2_AVAILABLE)
283
0
    if (HasAVX2())
284
0
        return 8 * BYTES_PER_ITERATION;
285
0
    else
286
0
#endif
287
0
#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)
288
0
    if (HasSSE2())
289
0
        return 4*BYTES_PER_ITERATION;
290
0
    else
291
0
#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
0
        return BYTES_PER_ITERATION;
303
0
}
304
305
ANONYMOUS_NAMESPACE_END
306
307
NAMESPACE_BEGIN(CryptoPP)
308
309
////////////////////////////// Bernstein ChaCha //////////////////////////////
310
311
std::string ChaCha_Policy::AlgorithmName() const
312
0
{
313
0
    return std::string("ChaCha")+IntToString(m_rounds);
314
0
}
315
316
std::string ChaCha_Policy::AlgorithmProvider() const
317
0
{
318
0
    return ChaCha_AlgorithmProvider();
319
0
}
320
321
void ChaCha_Policy::CipherSetKey(const NameValuePairs &params, const byte *key, size_t length)
322
0
{
323
0
    CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 16 || length == 32);
324
0
    CRYPTOPP_UNUSED(key); CRYPTOPP_UNUSED(length);
325
326
    // Use previous rounds as the default value
327
0
    int rounds = params.GetIntValueWithDefault(Name::Rounds(), m_rounds);
328
0
    if (rounds != 20 && rounds != 12 && rounds != 8)
329
0
        throw InvalidRounds(ChaCha::StaticAlgorithmName(), rounds);
330
331
    // Latch a good value
332
0
    m_rounds = rounds;
333
334
    // "expand 16-byte k" or "expand 32-byte k"
335
0
    m_state[0] = 0x61707865;
336
0
    m_state[1] = (length == 16) ? 0x3120646e : 0x3320646e;
337
0
    m_state[2] = (length == 16) ? 0x79622d36 : 0x79622d32;
338
0
    m_state[3] = 0x6b206574;
339
340
0
    GetBlock<word32, LittleEndian> get1(key);
341
0
    get1(m_state[4])(m_state[5])(m_state[6])(m_state[7]);
342
343
0
    GetBlock<word32, LittleEndian> get2(key + ((length == 32) ? 16 : 0));
344
0
    get2(m_state[8])(m_state[9])(m_state[10])(m_state[11]);
345
0
}
346
347
void ChaCha_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length)
348
0
{
349
0
    CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
350
0
    CRYPTOPP_ASSERT(length==8); CRYPTOPP_UNUSED(length);
351
352
0
    GetBlock<word32, LittleEndian> get(IV);
353
0
    m_state[12] = m_state[13] = 0;
354
0
    get(m_state[14])(m_state[15]);
355
0
}
356
357
void ChaCha_Policy::SeekToIteration(lword iterationCount)
358
0
{
359
0
    m_state[12] = (word32)iterationCount;  // low word
360
0
    m_state[13] = (word32)SafeRightShift<32>(iterationCount);
361
0
}
362
363
unsigned int ChaCha_Policy::GetAlignment() const
364
0
{
365
0
    return ChaCha_GetAlignment();
366
0
}
367
368
unsigned int ChaCha_Policy::GetOptimalBlockSize() const
369
0
{
370
0
    return ChaCha_GetOptimalBlockSize();
371
0
}
372
373
void ChaCha_Policy::OperateKeystream(KeystreamOperation operation,
374
        byte *output, const byte *input, size_t iterationCount)
375
0
{
376
0
    ChaCha_OperateKeystream(operation, m_state, m_state[12], m_state[13],
377
0
        m_rounds, output, input, iterationCount);
378
0
}
379
380
////////////////////////////// IETF ChaChaTLS //////////////////////////////
381
382
std::string ChaChaTLS_Policy::AlgorithmName() const
383
0
{
384
0
    return std::string("ChaChaTLS");
385
0
}
386
387
std::string ChaChaTLS_Policy::AlgorithmProvider() const
388
0
{
389
0
    return ChaCha_AlgorithmProvider();
390
0
}
391
392
void ChaChaTLS_Policy::CipherSetKey(const NameValuePairs &params, const byte *key, size_t length)
393
72
{
394
72
    CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 32);
395
72
    CRYPTOPP_UNUSED(length);
396
397
    // ChaChaTLS is always 20 rounds. Fetch Rounds() to avoid a spurious failure.
398
72
    int rounds = params.GetIntValueWithDefault(Name::Rounds(), ROUNDS);
399
72
    if (rounds != 20)
400
0
        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
72
    word64 block;
410
72
    if (params.GetValue("InitialBlock", block))
411
72
        m_counter = static_cast<word32>(block);
412
0
    else
413
0
        m_counter = 0;
414
415
    // State words are defined in RFC 8439, Section 2.3. Key is 32-bytes.
416
72
    GetBlock<word32, LittleEndian> get(key);
417
72
    get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3])
418
72
        (m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]);
419
72
}
420
421
void ChaChaTLS_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length)
422
72
{
423
72
    CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
424
72
    CRYPTOPP_ASSERT(length==12);
425
426
    // State words are defined in RFC 8439, Section 2.3.
427
72
    m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
428
72
    m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
429
430
    // Copy saved key into state
431
72
    std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32));
432
433
    // State words are defined in RFC 8439, Section 2.3
434
72
    GetBlock<word32, LittleEndian> get(IV);
435
72
    m_state[12] = m_counter;
436
72
    get(m_state[13])(m_state[14])(m_state[15]);
437
72
}
438
439
void ChaChaTLS_Policy::SeekToIteration(lword iterationCount)
440
0
{
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
0
    CRYPTOPP_ASSERT(iterationCount <= (std::numeric_limits<word32>::max)());
446
0
    m_state[12] = (word32)iterationCount;  // low word
447
0
}
448
449
unsigned int ChaChaTLS_Policy::GetAlignment() const
450
98
{
451
98
    return ChaCha_GetAlignment();
452
98
}
453
454
unsigned int ChaChaTLS_Policy::GetOptimalBlockSize() const
455
0
{
456
0
    return ChaCha_GetOptimalBlockSize();
457
0
}
458
459
void ChaChaTLS_Policy::OperateKeystream(KeystreamOperation operation,
460
        byte *output, const byte *input, size_t iterationCount)
461
49
{
462
49
    word32 discard=0;
463
49
    ChaCha_OperateKeystream(operation, m_state, m_state[12], discard,
464
49
            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
49
}
475
476
////////////////////////////// IETF XChaCha20 //////////////////////////////
477
478
std::string XChaCha20_Policy::AlgorithmName() const
479
0
{
480
0
    return std::string("XChaCha20");
481
0
}
482
483
std::string XChaCha20_Policy::AlgorithmProvider() const
484
0
{
485
0
    return ChaCha_AlgorithmProvider();
486
0
}
487
488
void XChaCha20_Policy::CipherSetKey(const NameValuePairs &params, const byte *key, size_t length)
489
0
{
490
0
    CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 32);
491
0
    CRYPTOPP_UNUSED(length);
492
493
    // Use previous rounds as the default value
494
0
    int rounds = params.GetIntValueWithDefault(Name::Rounds(), m_rounds);
495
0
    if (rounds != 20 && rounds != 12)
496
0
        throw InvalidRounds(ChaCha::StaticAlgorithmName(), rounds);
497
498
    // Latch a good value
499
0
    m_rounds = rounds;
500
501
0
    word64 block;
502
0
    if (params.GetValue("InitialBlock", block))
503
0
        m_counter = static_cast<word32>(block);
504
0
    else
505
0
        m_counter = 1;
506
507
    // Stash key away for use in CipherResynchronize
508
0
    GetBlock<word32, LittleEndian> get(key);
509
0
    get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3])
510
0
        (m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]);
511
0
}
512
513
void XChaCha20_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length)
514
0
{
515
0
    CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
516
0
    CRYPTOPP_ASSERT(length==24);
517
518
    // HChaCha derivation
519
0
    m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
520
0
    m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
521
522
    // Copy saved key into state
523
0
    std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32));
524
525
0
    GetBlock<word32, LittleEndian> get(iv);
526
0
    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
0
    HChaCha_OperateKeystream(m_state, m_state+4);
532
533
    // XChaCha state
534
0
    m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
535
0
    m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
536
537
    // Setup new IV
538
0
    m_state[12] = m_counter;
539
0
    m_state[13] = 0;
540
0
    m_state[14] = GetWord<word32>(false, LITTLE_ENDIAN_ORDER, iv+16);
541
0
    m_state[15] = GetWord<word32>(false, LITTLE_ENDIAN_ORDER, iv+20);
542
0
}
543
544
void XChaCha20_Policy::SeekToIteration(lword iterationCount)
545
0
{
546
    // Should we throw here??? XChaCha does not have a block
547
    // counter, so I'm not sure how to seek on it.
548
0
    CRYPTOPP_ASSERT(0); CRYPTOPP_UNUSED(iterationCount);
549
0
}
550
551
unsigned int XChaCha20_Policy::GetAlignment() const
552
0
{
553
0
    return ChaCha_GetAlignment();
554
0
}
555
556
unsigned int XChaCha20_Policy::GetOptimalBlockSize() const
557
0
{
558
0
    return ChaCha_GetOptimalBlockSize();
559
0
}
560
561
void XChaCha20_Policy::OperateKeystream(KeystreamOperation operation,
562
        byte *output, const byte *input, size_t iterationCount)
563
0
{
564
0
    ChaCha_OperateKeystream(operation, m_state, m_state[12], m_state[13],
565
0
            m_rounds, output, input, iterationCount);
566
0
}
567
568
NAMESPACE_END