Coverage Report

Created: 2024-11-21 07:03

/src/SymCrypt/lib/chacha20.c
Line
Count
Source (jump to first uncovered line)
1
//
2
// ChaCha20.c
3
//
4
// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
5
//
6
7
#include "precomp.h"
8
9
VOID
10
SYMCRYPT_CALL
11
SymCryptChaCha20CryptBlocks(
12
    _Inout_                 PSYMCRYPT_CHACHA20_STATE    pState,
13
    _In_reads_( cbData )    PCBYTE                      pbSrc,
14
    _Out_writes_( cbData )  PBYTE                       pbDst,
15
                            SIZE_T                      cbData );
16
// Encrypt Src to Dst using whole blocks, starting at block floor(pState->offset/64).
17
// # blocks processed is floor( cbData / 64 )
18
// pState->offset pointis updated by 64 for each block encrypted
19
20
21
22
#define OFFSET_MASK     (((UINT64)1 << 38) - 1)
23
24
SYMCRYPT_ERROR
25
SYMCRYPT_CALL
26
SymCryptChaCha20Init(
27
    _Out_                   PSYMCRYPT_CHACHA20_STATE    pState,
28
    _In_reads_( cbKey )     PCBYTE                      pbKey,
29
    _In_                    SIZE_T                      cbKey,
30
    _In_reads_( cbNonce )   PCBYTE                      pbNonce,
31
                            SIZE_T                      cbNonce,
32
                            UINT64                      offset )
33
0
{
34
0
    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
35
36
0
    if (cbKey != 32)
37
0
    {
38
0
        scError = SYMCRYPT_WRONG_KEY_SIZE;
39
0
        goto cleanup;
40
0
    }
41
42
0
    if (cbNonce != 12)
43
0
    {
44
0
        scError = SYMCRYPT_WRONG_NONCE_SIZE;
45
0
        goto cleanup;
46
0
    }
47
48
0
    SymCryptLsbFirstToUint32( pbKey, &pState->key[0], 8 );
49
0
    SymCryptLsbFirstToUint32( pbNonce, &pState->nonce[0], 3 );
50
51
0
    SymCryptChaCha20SetOffset( pState, offset );
52
53
0
cleanup:
54
0
    return scError;
55
0
}
56
57
VOID
58
SYMCRYPT_CALL
59
SymCryptChaCha20SetOffset(
60
    _Inout_                 PSYMCRYPT_CHACHA20_STATE    pState,
61
                            UINT64                      offset )
62
0
{
63
0
    pState->offset = offset;
64
0
    pState->keystreamBufferValid = FALSE;
65
0
}
66
67
VOID
68
SYMCRYPT_CALL
69
SymCryptChaCha20Crypt(
70
    _Inout_                 PSYMCRYPT_CHACHA20_STATE    pState,
71
    _In_reads_( cbData )    PCBYTE                      pbSrc,
72
    _Out_writes_( cbData )  PBYTE                       pbDst,
73
                            SIZE_T                      cbData )
74
0
{
75
0
    UINT32  blockOffset;
76
0
    SIZE_T  nBytes;
77
78
0
    blockOffset = pState->offset & 0x3f;
79
80
    // If the offset is in the middle of the block, we first crypt until the end
81
    // of the block
82
0
    if( blockOffset != 0 )
83
0
    {
84
0
        if( !pState->keystreamBufferValid )
85
0
        {
86
            // Generate a block of key stream
87
0
            SymCryptWipe( &pState->keystream[0], 64 );
88
0
            SymCryptChaCha20CryptBlocks(    pState,
89
0
                                            &pState->keystream[0],
90
0
                                            &pState->keystream[0],
91
0
                                            64 );
92
0
            pState->offset -= 64;   // Don't update the offset yet
93
0
        }
94
95
0
        nBytes = 64 - blockOffset;  // # bytes in buffer starting at offset
96
0
        if( cbData < nBytes )
97
0
        {
98
            // We don't use the generated block to the end. The buffer will be valid
99
            // at the end as the offset won't advance beyond the block.
100
0
            nBytes = cbData;
101
0
            pState->keystreamBufferValid = TRUE;
102
0
        } else {
103
            // We'll use the rest of the generated block. After that the key stream
104
            // buffer won't be valid as the offset will advance beyond it.
105
0
            pState->keystreamBufferValid = FALSE;
106
0
        }
107
108
0
        SymCryptXorBytes( pbSrc, &pState->keystream[ blockOffset ], pbDst, nBytes );
109
0
        pbSrc += nBytes;
110
0
        pbDst += nBytes;
111
0
        cbData -= nBytes;
112
0
        pState->offset += nBytes;
113
0
    }
114
115
    // Here: pbSrc, pbDst, cbData, and pState->offset all in sync
116
    // and either cbData == 0 or offset is at a block boundary
117
118
0
    if( cbData >= 64 )
119
0
    {
120
0
        nBytes = cbData & ~0x3f;
121
0
        SymCryptChaCha20CryptBlocks( pState, pbSrc, pbDst, nBytes );
122
0
        pbSrc += nBytes;
123
0
        pbDst += nBytes;
124
0
        cbData -= nBytes;
125
0
    }
126
127
0
    if( cbData > 0 )
128
0
    {
129
        // Generate a block of key stream
130
0
        SymCryptWipe( &pState->keystream[0], 64 );
131
0
        SymCryptChaCha20CryptBlocks(    pState,
132
0
                                        &pState->keystream[0],
133
0
                                        &pState->keystream[0],
134
0
                                        64 );
135
0
        pState->offset -= 64;   // Don't update the offset yet
136
0
        pState->keystreamBufferValid = TRUE;
137
138
0
        SymCryptXorBytes( pbSrc, &pState->keystream[0], pbDst, cbData );
139
0
        pState->offset += cbData;
140
        // The following updates are correct but not needed
141
        // pbSrc += cbData;
142
        // pbDst += cbData;
143
        // cbData -= cbData;
144
0
    }
145
0
}
146
147
0
#define CHACHA_QUARTERROUND( a, b, c, d ) { \
148
0
    a += b; d ^= a; d = ROL32( d, 16 ); \
149
0
    c += d; b ^= c; b = ROL32( b, 12 ); \
150
0
    a += b; d ^= a; d = ROL32( d, 8 ); \
151
0
    c += d; b ^= c; b = ROL32( b, 7 ); \
152
0
}
153
154
VOID
155
SYMCRYPT_CALL
156
SymCryptChaCha20CryptBlocks(
157
    _Inout_                 PSYMCRYPT_CHACHA20_STATE    pState,
158
    _In_reads_( cbData )    PCBYTE                      pbSrc,
159
    _Out_writes_( cbData )  PBYTE                       pbDst,
160
                            SIZE_T                      cbData )
161
0
{
162
0
    UINT32 counter;
163
0
    UINT32 s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15;
164
0
    int i;
165
166
0
    counter = (UINT32)(pState->offset >> 6);
167
168
0
    while( cbData >= 64 )
169
0
    {
170
        // Initialize the state
171
0
        s0  = 0x61707865;
172
0
        s1  = 0x3320646e;
173
0
        s2  = 0x79622d32;
174
0
        s3  = 0x6b206574;
175
0
        s4  = pState->key[0];
176
0
        s5  = pState->key[1];
177
0
        s6  = pState->key[2];
178
0
        s7  = pState->key[3];
179
0
        s8  = pState->key[4];
180
0
        s9  = pState->key[5];
181
0
        s10 = pState->key[6];
182
0
        s11 = pState->key[7];
183
0
        s12 = counter;
184
0
        s13 = pState->nonce[0];
185
0
        s14 = pState->nonce[1];
186
0
        s15 = pState->nonce[2];
187
188
0
        for( i=0; i<10; i++ )
189
0
        {
190
0
            CHACHA_QUARTERROUND( s0 , s4 , s8 , s12 );
191
0
            CHACHA_QUARTERROUND( s1 , s5 , s9 , s13 );
192
0
            CHACHA_QUARTERROUND( s2 , s6 , s10, s14 );
193
0
            CHACHA_QUARTERROUND( s3 , s7 , s11, s15 );
194
195
0
            CHACHA_QUARTERROUND( s0 , s5 , s10, s15 );
196
0
            CHACHA_QUARTERROUND( s1 , s6 , s11, s12 );
197
0
            CHACHA_QUARTERROUND( s2 , s7 , s8 , s13 );
198
0
            CHACHA_QUARTERROUND( s3 , s4 , s9 , s14 );
199
0
        }
200
201
0
        s0  += 0x61707865;
202
0
        s1  += 0x3320646e;
203
0
        s2  += 0x79622d32;
204
0
        s3  += 0x6b206574;
205
0
        s4  += pState->key[0];
206
0
        s5  += pState->key[1];
207
0
        s6  += pState->key[2];
208
0
        s7  += pState->key[3];
209
0
        s8  += pState->key[4];
210
0
        s9  += pState->key[5];
211
0
        s10 += pState->key[6];
212
0
        s11 += pState->key[7];
213
0
        s12 += counter;
214
0
        s13 += pState->nonce[0];
215
0
        s14 += pState->nonce[1];
216
0
        s15 += pState->nonce[2];
217
218
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst +  0, s0  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc +  0 ) );
219
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst +  4, s1  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc +  4 ) );
220
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst +  8, s2  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc +  8 ) );
221
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 12, s3  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 12 ) );
222
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 16, s4  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 16 ) );
223
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 20, s5  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 20 ) );
224
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 24, s6  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 24 ) );
225
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 28, s7  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 28 ) );
226
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 32, s8  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 32 ) );
227
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 36, s9  ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 36 ) );
228
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 40, s10 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 40 ) );
229
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 44, s11 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 44 ) );
230
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 48, s12 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 48 ) );
231
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 52, s13 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 52 ) );
232
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 56, s14 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 56 ) );
233
0
        SYMCRYPT_STORE_LSBFIRST32( pbDst + 60, s15 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 60 ) );
234
235
0
        counter ++;
236
        // If counter overflows then the caller has encrypted more than 256GB of data with a single stream, which is
237
        // called out as being insecure. It is the caller's responsibility to avoid this!
238
0
        pbSrc += 64;
239
0
        pbDst += 64;
240
0
        cbData -= 64;
241
0
        pState->offset += 64;
242
0
    }
243
0
}
244
245
static const BYTE   chacha20KatAnswer[ 3 ] = { 0xb5, 0xe0, 0x54 };
246
247
VOID
248
SYMCRYPT_CALL
249
SymCryptChaCha20Selftest(void)
250
0
{
251
0
    BYTE buf[3];
252
0
    SYMCRYPT_CHACHA20_STATE  state;
253
254
0
    SymCryptChaCha20Init(   &state,
255
0
                            SymCryptTestKey32, sizeof( SymCryptTestKey32 ),
256
0
                            SymCryptTestMsg16, 12,
257
0
                            0 );
258
259
0
    SymCryptChaCha20Crypt( &state, SymCryptTestMsg3, buf, sizeof( buf ) );
260
261
0
    SymCryptInjectError( buf, sizeof( buf ) );
262
263
0
    if( memcmp( buf, chacha20KatAnswer, sizeof( buf )) != 0 )
264
0
    {
265
0
        SymCryptFatal( 'Cha2' );
266
0
    }
267
0
}
268