Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/symcrypt/lib/chacha20.c
15010 views
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 point is 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
{
34
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
35
36
if (cbKey != 32)
37
{
38
scError = SYMCRYPT_WRONG_KEY_SIZE;
39
goto cleanup;
40
}
41
42
if (cbNonce != 12)
43
{
44
scError = SYMCRYPT_WRONG_NONCE_SIZE;
45
goto cleanup;
46
}
47
48
SymCryptLsbFirstToUint32( pbKey, &pState->key[0], 8 );
49
SymCryptLsbFirstToUint32( pbNonce, &pState->nonce[0], 3 );
50
51
SymCryptChaCha20SetOffset( pState, offset );
52
53
cleanup:
54
return scError;
55
}
56
57
VOID
58
SYMCRYPT_CALL
59
SymCryptChaCha20SetOffset(
60
_Inout_ PSYMCRYPT_CHACHA20_STATE pState,
61
UINT64 offset )
62
{
63
pState->offset = offset;
64
pState->keystreamBufferValid = FALSE;
65
}
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
{
75
UINT32 blockOffset;
76
SIZE_T nBytes;
77
78
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
if( blockOffset != 0 )
83
{
84
if( !pState->keystreamBufferValid )
85
{
86
// Generate a block of key stream
87
SymCryptWipe( &pState->keystream[0], 64 );
88
SymCryptChaCha20CryptBlocks( pState,
89
&pState->keystream[0],
90
&pState->keystream[0],
91
64 );
92
pState->offset -= 64; // Don't update the offset yet
93
}
94
95
nBytes = 64 - blockOffset; // # bytes in buffer starting at offset
96
if( cbData < nBytes )
97
{
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
nBytes = cbData;
101
pState->keystreamBufferValid = TRUE;
102
} 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
pState->keystreamBufferValid = FALSE;
106
}
107
108
SymCryptXorBytes( pbSrc, &pState->keystream[ blockOffset ], pbDst, nBytes );
109
pbSrc += nBytes;
110
pbDst += nBytes;
111
cbData -= nBytes;
112
pState->offset += nBytes;
113
}
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
if( cbData >= 64 )
119
{
120
nBytes = cbData & ~0x3f;
121
SymCryptChaCha20CryptBlocks( pState, pbSrc, pbDst, nBytes );
122
pbSrc += nBytes;
123
pbDst += nBytes;
124
cbData -= nBytes;
125
}
126
127
if( cbData > 0 )
128
{
129
// Generate a block of key stream
130
SymCryptWipe( &pState->keystream[0], 64 );
131
SymCryptChaCha20CryptBlocks( pState,
132
&pState->keystream[0],
133
&pState->keystream[0],
134
64 );
135
pState->offset -= 64; // Don't update the offset yet
136
pState->keystreamBufferValid = TRUE;
137
138
SymCryptXorBytes( pbSrc, &pState->keystream[0], pbDst, cbData );
139
pState->offset += cbData;
140
// The following updates are correct but not needed
141
// pbSrc += cbData;
142
// pbDst += cbData;
143
// cbData -= cbData;
144
}
145
}
146
147
#define CHACHA_QUARTERROUND( a, b, c, d ) { \
148
a += b; d ^= a; d = ROL32( d, 16 ); \
149
c += d; b ^= c; b = ROL32( b, 12 ); \
150
a += b; d ^= a; d = ROL32( d, 8 ); \
151
c += d; b ^= c; b = ROL32( b, 7 ); \
152
}
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
{
162
UINT32 counter;
163
UINT32 s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15;
164
int i;
165
166
counter = (UINT32)(pState->offset >> 6);
167
168
while( cbData >= 64 )
169
{
170
// Initialize the state
171
s0 = 0x61707865;
172
s1 = 0x3320646e;
173
s2 = 0x79622d32;
174
s3 = 0x6b206574;
175
s4 = pState->key[0];
176
s5 = pState->key[1];
177
s6 = pState->key[2];
178
s7 = pState->key[3];
179
s8 = pState->key[4];
180
s9 = pState->key[5];
181
s10 = pState->key[6];
182
s11 = pState->key[7];
183
s12 = counter;
184
s13 = pState->nonce[0];
185
s14 = pState->nonce[1];
186
s15 = pState->nonce[2];
187
188
for( i=0; i<10; i++ )
189
{
190
CHACHA_QUARTERROUND( s0 , s4 , s8 , s12 );
191
CHACHA_QUARTERROUND( s1 , s5 , s9 , s13 );
192
CHACHA_QUARTERROUND( s2 , s6 , s10, s14 );
193
CHACHA_QUARTERROUND( s3 , s7 , s11, s15 );
194
195
CHACHA_QUARTERROUND( s0 , s5 , s10, s15 );
196
CHACHA_QUARTERROUND( s1 , s6 , s11, s12 );
197
CHACHA_QUARTERROUND( s2 , s7 , s8 , s13 );
198
CHACHA_QUARTERROUND( s3 , s4 , s9 , s14 );
199
}
200
201
s0 += 0x61707865;
202
s1 += 0x3320646e;
203
s2 += 0x79622d32;
204
s3 += 0x6b206574;
205
s4 += pState->key[0];
206
s5 += pState->key[1];
207
s6 += pState->key[2];
208
s7 += pState->key[3];
209
s8 += pState->key[4];
210
s9 += pState->key[5];
211
s10 += pState->key[6];
212
s11 += pState->key[7];
213
s12 += counter;
214
s13 += pState->nonce[0];
215
s14 += pState->nonce[1];
216
s15 += pState->nonce[2];
217
218
SYMCRYPT_STORE_LSBFIRST32( pbDst + 0, s0 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 0 ) );
219
SYMCRYPT_STORE_LSBFIRST32( pbDst + 4, s1 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 4 ) );
220
SYMCRYPT_STORE_LSBFIRST32( pbDst + 8, s2 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 8 ) );
221
SYMCRYPT_STORE_LSBFIRST32( pbDst + 12, s3 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 12 ) );
222
SYMCRYPT_STORE_LSBFIRST32( pbDst + 16, s4 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 16 ) );
223
SYMCRYPT_STORE_LSBFIRST32( pbDst + 20, s5 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 20 ) );
224
SYMCRYPT_STORE_LSBFIRST32( pbDst + 24, s6 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 24 ) );
225
SYMCRYPT_STORE_LSBFIRST32( pbDst + 28, s7 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 28 ) );
226
SYMCRYPT_STORE_LSBFIRST32( pbDst + 32, s8 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 32 ) );
227
SYMCRYPT_STORE_LSBFIRST32( pbDst + 36, s9 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 36 ) );
228
SYMCRYPT_STORE_LSBFIRST32( pbDst + 40, s10 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 40 ) );
229
SYMCRYPT_STORE_LSBFIRST32( pbDst + 44, s11 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 44 ) );
230
SYMCRYPT_STORE_LSBFIRST32( pbDst + 48, s12 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 48 ) );
231
SYMCRYPT_STORE_LSBFIRST32( pbDst + 52, s13 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 52 ) );
232
SYMCRYPT_STORE_LSBFIRST32( pbDst + 56, s14 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 56 ) );
233
SYMCRYPT_STORE_LSBFIRST32( pbDst + 60, s15 ^ SYMCRYPT_LOAD_LSBFIRST32( pbSrc + 60 ) );
234
235
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
pbSrc += 64;
239
pbDst += 64;
240
cbData -= 64;
241
pState->offset += 64;
242
}
243
}
244
245
static const BYTE chacha20KatAnswer[ 3 ] = { 0xb5, 0xe0, 0x54 };
246
247
VOID
248
SYMCRYPT_CALL
249
SymCryptChaCha20Selftest(void)
250
{
251
BYTE buf[3];
252
SYMCRYPT_CHACHA20_STATE state;
253
254
SymCryptChaCha20Init( &state,
255
SymCryptTestKey32, sizeof( SymCryptTestKey32 ),
256
SymCryptTestMsg16, 12,
257
0 );
258
259
SymCryptChaCha20Crypt( &state, SymCryptTestMsg3, buf, sizeof( buf ) );
260
261
SymCryptInjectError( buf, sizeof( buf ) );
262
263
if( memcmp( buf, chacha20KatAnswer, sizeof( buf )) != 0 )
264
{
265
SymCryptFatal( 'Cha2' );
266
}
267
}
268
269