1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
|
From e9e4cb4ed045a12c984a367bee0ddcbb3f27ae0d Mon Sep 17 00:00:00 2001
From: mancha <mancha1@hush.com>
Date: Sun, 29 Sep 2013
Subject: CVE-2013-1619 and CVE-2013-2116 [GNUTLS-SA-2013-1,GNUTLS-SA-2013-2]
Fix to avoid a timing attack in TLS CBC record parsing (aka Lucky 13).
For background, see http://www.isg.rhul.ac.uk/tls/Lucky13.html
The fix for CVE-2013-2116 is folded into this patch since it addresses
a problem introduced by the fix for CVE-2013-1619.
This is a backport adaptation for use with GnuTLS 2.8.4.
Relevant upstream commits:
--------------------------
https://gitorious.org/gnutls/gnutls/commit/458c67cf98740e
https://gitorious.org/gnutls/gnutls/commit/93b7fcfa3297a9
https://gitorious.org/gnutls/gnutls/commit/32a7367601a3fd
https://gitorious.org/gnutls/gnutls/commit/63a331df6aa0ec
---
gnutls_algorithms.c | 14 +++++
gnutls_algorithms.h | 3 +
gnutls_cipher.c | 132 ++++++++++++++++++++++++++++++++--------------------
gnutls_hash_int.h | 21 ++++++++
4 files changed, 121 insertions(+), 49 deletions(-)
--- a/lib/gnutls_cipher.c 2013-09-27
+++ b/lib/gnutls_cipher.c 2013-09-27
@@ -287,6 +287,26 @@ calc_enc_length (gnutls_session_t sessio
return length;
}
+#define PREAMBLE_SIZE 16
+static inline int make_preamble(opaque* uint64_data, opaque type, uint16_t c_length, opaque ver, opaque* preamble)
+{
+ opaque minor = _gnutls_version_get_minor (ver);
+ opaque major = _gnutls_version_get_major (ver);
+ opaque *p = preamble;
+
+ memcpy(p, uint64_data, 8);
+ p+=8;
+ *p=type; p++;
+ if (_gnutls_version_has_variable_padding (ver))
+ { /* TLS 1.0 or higher */
+ *p = major; p++;
+ *p = minor; p++;
+ }
+ memcpy(p, &c_length, 2);
+ p+=2;
+ return p-preamble;
+}
+
/* This is the actual encryption
* Encrypts the given compressed datum, and puts the result to cipher_data,
* which has cipher_size size.
@@ -304,11 +324,11 @@ _gnutls_compressed2ciphertext (gnutls_se
int length, ret;
digest_hd_st td;
uint8_t type = _type;
- uint8_t major, minor;
+ opaque preamble[PREAMBLE_SIZE];
+ int preamble_size;
int hash_size =
_gnutls_hash_get_algo_len (session->security_parameters.
write_mac_algorithm);
- gnutls_protocol_t ver;
int blocksize =
_gnutls_cipher_get_block_size (session->security_parameters.
write_bulk_cipher_algorithm);
@@ -316,40 +336,27 @@ _gnutls_compressed2ciphertext (gnutls_se
_gnutls_cipher_is_block (session->security_parameters.
write_bulk_cipher_algorithm);
opaque *data_ptr;
-
-
- ver = gnutls_protocol_get_version (session);
- minor = _gnutls_version_get_minor (ver);
- major = _gnutls_version_get_major (ver);
-
+ int ver = gnutls_protocol_get_version (session);
/* Initialize MAC */
- ret = mac_init (&td, session->security_parameters.write_mac_algorithm,
- session->connection_state.write_mac_secret.data,
- session->connection_state.write_mac_secret.size, ver);
-
- if (ret < 0
- && session->security_parameters.write_mac_algorithm != GNUTLS_MAC_NULL)
- {
- gnutls_assert ();
- return ret;
- }
c_length = _gnutls_conv_uint16 (compressed.size);
if (session->security_parameters.write_mac_algorithm != GNUTLS_MAC_NULL)
{ /* actually when the algorithm in not the NULL one */
- _gnutls_hmac (&td,
- UINT64DATA (session->connection_state.
- write_sequence_number), 8);
-
- _gnutls_hmac (&td, &type, 1);
- if (ver >= GNUTLS_TLS1)
- { /* TLS 1.0 or higher */
- _gnutls_hmac (&td, &major, 1);
- _gnutls_hmac (&td, &minor, 1);
- }
- _gnutls_hmac (&td, &c_length, 2);
+ digest_hd_st td;
+
+ ret = mac_init (&td, session->security_parameters.write_mac_algorithm,
+ session->connection_state.write_mac_secret.data,
+ session->connection_state.write_mac_secret.size, ver);
+
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+ preamble_size = make_preamble( UINT64DATA (session->connection_state.write_sequence_number), type, c_length, ver, preamble);
+ _gnutls_hmac (&td, preamble, preamble_size);
_gnutls_hmac (&td, compressed.data, compressed.size);
mac_deinit (&td, MAC, ver);
}
@@ -418,6 +425,49 @@ _gnutls_compressed2ciphertext (gnutls_se
return length;
}
+static void dummy_wait(gnutls_session_t session, gnutls_datum_t* plaintext,
+ unsigned pad_failed, unsigned int pad, unsigned total, int ver)
+{
+ /* this hack is only needed on CBC ciphers */
+ if (_gnutls_cipher_is_block (session->security_parameters.read_bulk_cipher_algorithm) == CIPHER_BLOCK)
+ {
+ uint8_t MAC[MAX_HASH_SIZE];
+ unsigned len;
+ digest_hd_st td;
+ int ret;
+
+ ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
+ session->connection_state.read_mac_secret.data,
+ session->connection_state.read_mac_secret.size, ver);
+
+ if (ret < 0)
+ return;
+
+ /* force an additional hash compression function evaluation to prevent timing
+ * attacks that distinguish between wrong-mac + correct pad, from wrong-mac + incorrect pad.
+ */
+ if (pad_failed == 0 && pad > 0)
+ {
+ len = _gnutls_get_hash_block_len(session->security_parameters.read_mac_algorithm);
+ if (len > 0)
+ {
+ /* This is really specific to the current hash functions.
+ * It should be removed once a protocol fix is in place.
+ */
+ if ((pad+total) % len > len-9 && total % len <= len-9)
+ {
+ if (len < plaintext->size)
+ _gnutls_hmac (&td, plaintext->data, len);
+ else
+ _gnutls_hmac (&td, plaintext->data, plaintext->size);
+ }
+ }
+ }
+
+ mac_deinit (&td, MAC, ver);
+ }
+}
+
/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size.
* Returns the actual compressed packet size.
*/
@@ -429,38 +479,22 @@ _gnutls_ciphertext2compressed (gnutls_se
{
uint8_t MAC[MAX_HASH_SIZE];
uint16_t c_length;
- uint8_t pad;
+ unsigned int pad = 0;
int length;
digest_hd_st td;
uint16_t blocksize;
int ret, i, pad_failed = 0;
- uint8_t major, minor;
- gnutls_protocol_t ver;
+ opaque preamble[PREAMBLE_SIZE];
+ int preamble_size = 0;
+ int ver = gnutls_protocol_get_version (session);
int hash_size =
_gnutls_hash_get_algo_len (session->security_parameters.
read_mac_algorithm);
- ver = gnutls_protocol_get_version (session);
- minor = _gnutls_version_get_minor (ver);
- major = _gnutls_version_get_major (ver);
-
blocksize =
_gnutls_cipher_get_block_size (session->security_parameters.
read_bulk_cipher_algorithm);
- /* initialize MAC
- */
- ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
- session->connection_state.read_mac_secret.data,
- session->connection_state.read_mac_secret.size, ver);
-
- if (ret < 0
- && session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
- {
- gnutls_assert ();
- return GNUTLS_E_INTERNAL_ERROR;
- }
-
/* actual decryption (inplace)
*/
switch (_gnutls_cipher_is_block
@@ -508,31 +542,25 @@ _gnutls_ciphertext2compressed (gnutls_se
gnutls_assert ();
return GNUTLS_E_DECRYPTION_FAILED;
}
- pad = ciphertext.data[ciphertext.size - 1] + 1; /* pad */
-
- if ((int) pad > (int) ciphertext.size - hash_size)
- {
- gnutls_assert ();
- _gnutls_record_log
- ("REC[%p]: Short record length %d > %d - %d (under attack?)\n",
- session, pad, ciphertext.size, hash_size);
- /* We do not fail here. We check below for the
- * the pad_failed. If zero means success.
- */
- pad_failed = GNUTLS_E_DECRYPTION_FAILED;
- }
-
- length = ciphertext.size - hash_size - pad;
-
- /* Check the pading bytes (TLS 1.x)
+ pad = ciphertext.data[ciphertext.size - 1]; /* pad */
+ if (pad+1 > ciphertext.size-hash_size)
+ pad_failed = GNUTLS_E_DECRYPTION_FAILED;
+
+ /* Check the pading bytes (TLS 1.x).
+ * Note that we access all 256 bytes of ciphertext for padding check
+ * because there is a timing channel in that memory access (in certain CPUs
*/
if (ver >= GNUTLS_TLS1 && pad_failed == 0)
for (i = 2; i < pad; i++)
{
- if (ciphertext.data[ciphertext.size - i] !=
- ciphertext.data[ciphertext.size - 1])
+ if (ciphertext.data[ciphertext.size - i] != pad)
pad_failed = GNUTLS_E_DECRYPTION_FAILED;
}
+
+ if (pad_failed)
+ pad = 0;
+ length = ciphertext.size - hash_size - pad - 1;
+
break;
default:
gnutls_assert ();
@@ -548,17 +576,20 @@ _gnutls_ciphertext2compressed (gnutls_se
*/
if (session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
{
- _gnutls_hmac (&td,
- UINT64DATA (session->connection_state.
- read_sequence_number), 8);
-
- _gnutls_hmac (&td, &type, 1);
- if (ver >= GNUTLS_TLS1)
- { /* TLS 1.x */
- _gnutls_hmac (&td, &major, 1);
- _gnutls_hmac (&td, &minor, 1);
- }
- _gnutls_hmac (&td, &c_length, 2);
+ digest_hd_st td;
+
+ ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
+ session->connection_state.read_mac_secret.data,
+ session->connection_state.read_mac_secret.size, ver);
+
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ preamble_size = make_preamble( UINT64DATA (session->connection_state.read_sequence_number), type, c_length, ver, preamble);
+ _gnutls_hmac (&td, preamble, preamble_size);
if (length > 0)
_gnutls_hmac (&td, ciphertext.data, length);
@@ -566,16 +597,14 @@ _gnutls_ciphertext2compressed (gnutls_se
mac_deinit (&td, MAC, ver);
}
- /* This one was introduced to avoid a timing attack against the TLS
- * 1.0 protocol.
- */
- if (pad_failed != 0)
- return pad_failed;
-
/* HMAC was not the same.
*/
- if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0)
+ if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0 || pad_failed != 0)
{
+ gnutls_datum_t compressed = {compress_data, compress_size};
+ /* HMAC was not the same. */
+ dummy_wait(session, &compressed, pad_failed, pad, length+preamble_size, ver);
+
gnutls_assert ();
return GNUTLS_E_DECRYPTION_FAILED;
}
--- a/lib/gnutls_hash_int.h 2013-09-27
+++ b/lib/gnutls_hash_int.h 2013-09-27
@@ -92,4 +92,25 @@ void _gnutls_mac_deinit_ssl3_handshake (
int _gnutls_hash_copy (digest_hd_st* dst_handle, digest_hd_st * src_handle);
+/* We shouldn't need to know that, but a work-around in decoding
+ * TLS record padding requires that.
+ */
+inline static size_t
+_gnutls_get_hash_block_len (gnutls_digest_algorithm_t algo)
+{
+ switch (algo)
+ {
+ case GNUTLS_DIG_MD5:
+ case GNUTLS_DIG_SHA1:
+ case GNUTLS_DIG_RMD160:
+ case GNUTLS_DIG_SHA256:
+ case GNUTLS_DIG_SHA384:
+ case GNUTLS_DIG_SHA512:
+ case GNUTLS_DIG_SHA224:
+ return 64;
+ default:
+ return 0;
+ }
+}
+
#endif /* GNUTLS_HASH_INT_H */
--- a/lib/gnutls_algorithms.c 2013-09-27
+++ b/lib/gnutls_algorithms.c 2013-09-27
@@ -1185,6 +1185,20 @@ _gnutls_version_is_supported (gnutls_ses
return 1;
}
+/* This function determines if the version specified can have
+ non-minimal padding. */
+int _gnutls_version_has_variable_padding (gnutls_protocol_t version)
+{
+ switch(version) {
+ case GNUTLS_TLS1_0:
+ case GNUTLS_TLS1_1:
+ case GNUTLS_TLS1_2:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
/* Type to KX mappings */
gnutls_kx_algorithm_t
_gnutls_map_kx_get_kx (gnutls_credentials_type_t type, int server)
--- a/lib/gnutls_algorithms.h 2013-09-27
+++ b/lib/gnutls_algorithms.h 2013-09-27
@@ -41,6 +41,9 @@ int _gnutls_version_get_major (gnutls_pr
int _gnutls_version_get_minor (gnutls_protocol_t ver);
gnutls_protocol_t _gnutls_version_get (int major, int minor);
+/* Functions for feature checks */
+int _gnutls_version_has_variable_padding (gnutls_protocol_t version);
+
/* Functions for MACs. */
int _gnutls_mac_is_ok (gnutls_mac_algorithm_t algorithm);
gnutls_mac_algorithm_t _gnutls_x509_oid2mac_algorithm (const char *oid);
|