Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/symcrypt/lib/chacha20_poly1305.c
15010 views
1
//
2
// ChaCha20_Poly1305.c
3
//
4
// Copyright (c) Microsoft Corporation.
5
//
6
7
#include "precomp.h"
8
9
#define CHACHA20_POLY1305_MAX_DATA_SIZE (((1ull << 32) - 1) * 64)
10
11
// Compile time BOOL statically determines if we need to check cbData > CHACHA20_POLY1305_MAX_DATA_SIZE
12
// Used to suppress MSVC C4127 and clang Wtautological-constant-out-of-range-compare on 32b platforms
13
const BOOL fcbDataLteMaxDataSizeStatic = SIZE_T_MAX <= CHACHA20_POLY1305_MAX_DATA_SIZE;
14
15
VOID
16
SYMCRYPT_CALL
17
SymCryptChaCha20Poly1305ComputeTag(
18
_Inout_ PSYMCRYPT_POLY1305_STATE pState,
19
_In_reads_opt_( cbAuthData ) PCBYTE pbAuthData,
20
SIZE_T cbAuthData,
21
_In_reads_( cbData ) PCBYTE pbData,
22
SIZE_T cbData,
23
_Out_writes_( SYMCRYPT_POLY1305_RESULT_SIZE ) PBYTE pbTag )
24
{
25
SYMCRYPT_ALIGN BYTE buf[SYMCRYPT_POLY1305_BLOCK_SIZE];
26
BYTE partialBlockSize;
27
28
SymCryptWipeKnownSize( buf, SYMCRYPT_POLY1305_BLOCK_SIZE );
29
30
// Add additional authentication data if needed.
31
if ( cbAuthData > 0 )
32
{
33
SymCryptPoly1305Append( pState, pbAuthData, cbAuthData );
34
35
// Append zeros to make a complete Poly1305 block.
36
partialBlockSize = cbAuthData % SYMCRYPT_POLY1305_BLOCK_SIZE;
37
if ( partialBlockSize > 0 )
38
{
39
SymCryptPoly1305Append( pState, buf, SYMCRYPT_POLY1305_BLOCK_SIZE - partialBlockSize );
40
}
41
}
42
43
// Add ciphertext if needed.
44
if ( cbData > 0 )
45
{
46
SymCryptPoly1305Append( pState, pbData, cbData );
47
48
// Append zeros to make a complete Poly1305 block.
49
partialBlockSize = cbData % SYMCRYPT_POLY1305_BLOCK_SIZE;
50
if ( partialBlockSize > 0 )
51
{
52
SymCryptPoly1305Append( pState, buf, SYMCRYPT_POLY1305_BLOCK_SIZE - partialBlockSize );
53
}
54
}
55
56
// Add length of additional authentication data and ciphertext.
57
SYMCRYPT_STORE_LSBFIRST64( &buf[0], cbAuthData );
58
SYMCRYPT_STORE_LSBFIRST64( &buf[8], cbData );
59
SymCryptPoly1305Append( pState, buf, SYMCRYPT_POLY1305_BLOCK_SIZE );
60
SymCryptWipeKnownSize( buf, SYMCRYPT_POLY1305_BLOCK_SIZE );
61
62
SymCryptPoly1305Result( pState, pbTag );
63
}
64
65
SYMCRYPT_NOINLINE
66
SYMCRYPT_ERROR
67
SYMCRYPT_CALL
68
SymCryptChaCha20Poly1305Encrypt(
69
_In_reads_( cbKey ) PCBYTE pbKey,
70
SIZE_T cbKey,
71
_In_reads_( cbNonce ) PCBYTE pbNonce,
72
SIZE_T cbNonce,
73
_In_reads_opt_( cbAuthData ) PCBYTE pbAuthData,
74
SIZE_T cbAuthData,
75
_In_reads_( cbData ) PCBYTE pbSrc,
76
_Out_writes_( cbData ) PBYTE pbDst,
77
SIZE_T cbData,
78
_Out_writes_( cbTag ) PBYTE pbTag,
79
SIZE_T cbTag )
80
{
81
SYMCRYPT_ERROR status = SYMCRYPT_NO_ERROR;
82
SYMCRYPT_CHACHA20_STATE ChaCha20State;
83
SYMCRYPT_POLY1305_STATE Poly1305State;
84
SYMCRYPT_ALIGN BYTE key[SYMCRYPT_POLY1305_KEY_SIZE];
85
86
if ( !fcbDataLteMaxDataSizeStatic && cbData > CHACHA20_POLY1305_MAX_DATA_SIZE )
87
{
88
status = SYMCRYPT_WRONG_DATA_SIZE;
89
goto cleanup;
90
}
91
92
if ( cbTag != SYMCRYPT_POLY1305_RESULT_SIZE )
93
{
94
status = SYMCRYPT_WRONG_TAG_SIZE;
95
goto cleanup;
96
}
97
98
status = SymCryptChaCha20Init( &ChaCha20State, pbKey, cbKey, pbNonce, cbNonce, 0 );
99
if ( status != SYMCRYPT_NO_ERROR )
100
{
101
goto cleanup;
102
}
103
104
// Generate the first 32 bytes of keystream.
105
SymCryptWipeKnownSize( key, sizeof( key ) );
106
SymCryptChaCha20Crypt( &ChaCha20State, key, key, sizeof ( key ) );
107
108
// Create the Poly1305 key using the first 32 bytes of the ChaCha20 keystream.
109
SymCryptPoly1305Init( &Poly1305State, key );
110
SymCryptWipeKnownSize( key, sizeof( key ) );
111
112
// Encrypt data if needed.
113
if ( cbData > 0 )
114
{
115
// Advance the keystream to counter 1 (offset 64) for data encryption.
116
SymCryptChaCha20SetOffset( &ChaCha20State, 64 );
117
SymCryptChaCha20Crypt( &ChaCha20State, pbSrc, pbDst, cbData );
118
}
119
120
// We read the ciphertext back, violating the general rule not to rely on I/O buffers
121
// as they can reside in a different security domain. For ChaCha20Poly1305, like GCM,
122
// this read-back of data is not a problem. An attacker with access to the buffer
123
// will get the ChaCha20 key stream plus the Poly1305 authenticator of a single value.
124
// As Poly1305 is strong even with attacker-controlled data, this is harmless.
125
SymCryptChaCha20Poly1305ComputeTag( &Poly1305State, pbAuthData, cbAuthData,
126
pbDst, cbData, pbTag );
127
cleanup:
128
129
SymCryptWipeKnownSize( &ChaCha20State, sizeof( ChaCha20State ) );
130
SymCryptWipeKnownSize( &Poly1305State, sizeof( Poly1305State ) );
131
132
return status;
133
}
134
135
SYMCRYPT_NOINLINE
136
SYMCRYPT_ERROR
137
SYMCRYPT_CALL
138
SymCryptChaCha20Poly1305Decrypt(
139
_In_reads_( cbKey ) PCBYTE pbKey,
140
SIZE_T cbKey,
141
_In_reads_( cbNonce ) PCBYTE pbNonce,
142
SIZE_T cbNonce,
143
_In_reads_opt_( cbAuthData ) PCBYTE pbAuthData,
144
SIZE_T cbAuthData,
145
_In_reads_( cbData ) PCBYTE pbSrc,
146
_Out_writes_( cbData ) PBYTE pbDst,
147
SIZE_T cbData,
148
_In_reads_( cbTag ) PCBYTE pbTag,
149
SIZE_T cbTag )
150
{
151
SYMCRYPT_ERROR status = SYMCRYPT_NO_ERROR;
152
SYMCRYPT_CHACHA20_STATE ChaCha20State;
153
SYMCRYPT_POLY1305_STATE Poly1305State;
154
SYMCRYPT_ALIGN BYTE buf[SYMCRYPT_POLY1305_RESULT_SIZE];
155
SYMCRYPT_ALIGN BYTE key[SYMCRYPT_POLY1305_KEY_SIZE];
156
157
if ( !fcbDataLteMaxDataSizeStatic && cbData > CHACHA20_POLY1305_MAX_DATA_SIZE )
158
{
159
status = SYMCRYPT_WRONG_DATA_SIZE;
160
goto cleanup;
161
}
162
163
if ( cbTag != SYMCRYPT_POLY1305_RESULT_SIZE )
164
{
165
status = SYMCRYPT_WRONG_TAG_SIZE;
166
goto cleanup;
167
}
168
169
status = SymCryptChaCha20Init( &ChaCha20State, pbKey, cbKey, pbNonce, cbNonce, 0 );
170
if ( status != SYMCRYPT_NO_ERROR )
171
{
172
goto cleanup;
173
}
174
175
// Generate the first 32 bytes of keystream.
176
SymCryptWipeKnownSize( key, sizeof( key ) );
177
SymCryptChaCha20Crypt( &ChaCha20State, key, key, sizeof( key ) );
178
179
// Create the Poly1305 key using the first 32 bytes of the ChaCha20 keystream.
180
SymCryptPoly1305Init( &Poly1305State, key );
181
SymCryptWipeKnownSize( key, sizeof( key ) );
182
183
// We read the ciphertext back, violating the general rule not to rely on I/O buffers
184
// as they can reside in a different security domain. For ChaCha20Poly1305, like GCM,
185
// this read-back of data is not a problem. An attacker with access to the buffer
186
// will get the ChaCha20 key stream plus the Poly1305 authenticator of a single value.
187
// As Poly1305 is strong even with attacker-controlled data, this is harmless.
188
SymCryptChaCha20Poly1305ComputeTag( &Poly1305State, pbAuthData, cbAuthData,
189
pbSrc, cbData, buf );
190
191
// Validate tag.
192
if (!SymCryptEqual(pbTag, buf, cbTag))
193
{
194
status = SYMCRYPT_AUTHENTICATION_FAILURE;
195
goto cleanup;
196
}
197
198
// Decrypt data if needed.
199
if ( cbData > 0)
200
{
201
// Advance the keystream to counter 1 (offset 64) for data decryption.
202
SymCryptChaCha20SetOffset( &ChaCha20State, 64 );
203
SymCryptChaCha20Crypt( &ChaCha20State, pbSrc, pbDst, cbData );
204
}
205
206
cleanup:
207
208
SymCryptWipeKnownSize( &ChaCha20State, sizeof( ChaCha20State ) );
209
SymCryptWipeKnownSize( &Poly1305State, sizeof( Poly1305State ) );
210
211
return status;
212
}
213
214
215
static const BYTE SymCryptChaCha20Poly1305Result[3 + SYMCRYPT_POLY1305_RESULT_SIZE] =
216
{
217
0x5d, 0xba, 0x7b,
218
0x80, 0x10, 0xd2, 0x05, 0x4a, 0xad, 0x53, 0x1f, 0xa2, 0xce, 0x83, 0xc1, 0x66, 0x12, 0x85, 0x21
219
};
220
221
VOID
222
SYMCRYPT_CALL
223
SymCryptChaCha20Poly1305Selftest(void)
224
{
225
BYTE buf[3 + SYMCRYPT_POLY1305_RESULT_SIZE];
226
SYMCRYPT_ERROR err;
227
228
if ( SymCryptChaCha20Poly1305Encrypt( SymCryptTestKey32, sizeof( SymCryptTestKey32 ),
229
SymCryptTestMsg16, 12,
230
SymCryptTestMsg16, sizeof( SymCryptTestMsg16 ),
231
&SymCryptTestMsg3[0], buf, 3,
232
&buf[3], SYMCRYPT_POLY1305_RESULT_SIZE ) != SYMCRYPT_NO_ERROR )
233
{
234
SymCryptFatal( 'ccp0' );
235
}
236
237
SymCryptInjectError( buf, sizeof( buf ) );
238
if ( memcmp( buf, SymCryptChaCha20Poly1305Result, sizeof( buf ) ) != 0 )
239
{
240
SymCryptFatal( 'ccp1' );
241
}
242
243
// Inject error into the ciphertext or tag.
244
SymCryptInjectError( buf, sizeof( buf ) );
245
246
err = SymCryptChaCha20Poly1305Decrypt( SymCryptTestKey32, sizeof( SymCryptTestKey32 ),
247
SymCryptTestMsg16, 12,
248
SymCryptTestMsg16, sizeof( SymCryptTestMsg16 ),
249
buf, buf, 3,
250
&buf[3], SYMCRYPT_POLY1305_RESULT_SIZE );
251
SymCryptInjectError( buf, 3 );
252
253
if ( err != SYMCRYPT_NO_ERROR || memcmp( buf, SymCryptTestMsg3, 3 ) != 0 )
254
{
255
SymCryptFatal( 'ccp2' );
256
}
257
}
258
259