]>
Commit | Line | Data |
---|---|---|
6fc6879b | 1 | /* |
1622b331 | 2 | * TLSv1 Record Protocol |
61f1ed91 | 3 | * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> |
6fc6879b | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
03da66bd JM |
12 | #include "crypto/md5.h" |
13 | #include "crypto/sha1.h" | |
cdc6e5d0 | 14 | #include "crypto/sha256.h" |
6fc6879b JM |
15 | #include "tlsv1_common.h" |
16 | #include "tlsv1_record.h" | |
17 | ||
18 | ||
19 | /** | |
20 | * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite | |
21 | * @rl: Pointer to TLS record layer data | |
22 | * @cipher_suite: New cipher suite | |
23 | * Returns: 0 on success, -1 on failure | |
24 | * | |
25 | * This function is used to prepare TLS record layer for cipher suite change. | |
26 | * tlsv1_record_change_write_cipher() and | |
27 | * tlsv1_record_change_read_cipher() functions can then be used to change the | |
28 | * currently used ciphers. | |
29 | */ | |
30 | int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, | |
31 | u16 cipher_suite) | |
32 | { | |
33 | const struct tls_cipher_suite *suite; | |
34 | const struct tls_cipher_data *data; | |
35 | ||
36 | wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x", | |
37 | cipher_suite); | |
38 | rl->cipher_suite = cipher_suite; | |
39 | ||
40 | suite = tls_get_cipher_suite(cipher_suite); | |
41 | if (suite == NULL) | |
42 | return -1; | |
43 | ||
44 | if (suite->hash == TLS_HASH_MD5) { | |
45 | rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5; | |
46 | rl->hash_size = MD5_MAC_LEN; | |
47 | } else if (suite->hash == TLS_HASH_SHA) { | |
48 | rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1; | |
49 | rl->hash_size = SHA1_MAC_LEN; | |
cdc6e5d0 JM |
50 | } else if (suite->hash == TLS_HASH_SHA256) { |
51 | rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256; | |
52 | rl->hash_size = SHA256_MAC_LEN; | |
6fc6879b JM |
53 | } |
54 | ||
55 | data = tls_get_cipher_data(suite->cipher); | |
56 | if (data == NULL) | |
57 | return -1; | |
58 | ||
59 | rl->key_material_len = data->key_material; | |
60 | rl->iv_size = data->block_size; | |
61 | rl->cipher_alg = data->alg; | |
62 | ||
63 | return 0; | |
64 | } | |
65 | ||
66 | ||
67 | /** | |
68 | * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher | |
69 | * @rl: Pointer to TLS record layer data | |
70 | * Returns: 0 on success (cipher changed), -1 on failure | |
71 | * | |
72 | * This function changes TLS record layer to use the new cipher suite | |
73 | * configured with tlsv1_record_set_cipher_suite() for writing. | |
74 | */ | |
75 | int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl) | |
76 | { | |
77 | wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite " | |
78 | "0x%04x", rl->cipher_suite); | |
79 | rl->write_cipher_suite = rl->cipher_suite; | |
80 | os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN); | |
81 | ||
82 | if (rl->write_cbc) { | |
83 | crypto_cipher_deinit(rl->write_cbc); | |
84 | rl->write_cbc = NULL; | |
85 | } | |
86 | if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { | |
87 | rl->write_cbc = crypto_cipher_init(rl->cipher_alg, | |
88 | rl->write_iv, rl->write_key, | |
89 | rl->key_material_len); | |
90 | if (rl->write_cbc == NULL) { | |
91 | wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " | |
92 | "cipher"); | |
93 | return -1; | |
94 | } | |
95 | } | |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
100 | ||
101 | /** | |
102 | * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher | |
103 | * @rl: Pointer to TLS record layer data | |
104 | * Returns: 0 on success (cipher changed), -1 on failure | |
105 | * | |
106 | * This function changes TLS record layer to use the new cipher suite | |
107 | * configured with tlsv1_record_set_cipher_suite() for reading. | |
108 | */ | |
109 | int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) | |
110 | { | |
111 | wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite " | |
112 | "0x%04x", rl->cipher_suite); | |
113 | rl->read_cipher_suite = rl->cipher_suite; | |
114 | os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN); | |
115 | ||
116 | if (rl->read_cbc) { | |
117 | crypto_cipher_deinit(rl->read_cbc); | |
118 | rl->read_cbc = NULL; | |
119 | } | |
120 | if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { | |
121 | rl->read_cbc = crypto_cipher_init(rl->cipher_alg, | |
122 | rl->read_iv, rl->read_key, | |
123 | rl->key_material_len); | |
124 | if (rl->read_cbc == NULL) { | |
125 | wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " | |
126 | "cipher"); | |
127 | return -1; | |
128 | } | |
129 | } | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
134 | ||
135 | /** | |
136 | * tlsv1_record_send - TLS record layer: Send a message | |
137 | * @rl: Pointer to TLS record layer data | |
138 | * @content_type: Content type (TLS_CONTENT_TYPE_*) | |
3bff59f8 | 139 | * @buf: Buffer for the generated TLS message (needs to have extra space for |
5c47af9a | 140 | * header, IV (TLS v1.1), and HMAC) |
6fc6879b | 141 | * @buf_size: Maximum buf size |
3bff59f8 | 142 | * @payload: Payload to be sent |
6fc6879b JM |
143 | * @payload_len: Length of the payload |
144 | * @out_len: Buffer for returning the used buf length | |
145 | * Returns: 0 on success, -1 on failure | |
146 | * | |
147 | * This function fills in the TLS record layer header, adds HMAC, and encrypts | |
148 | * the data using the current write cipher. | |
149 | */ | |
150 | int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, | |
3bff59f8 JM |
151 | size_t buf_size, const u8 *payload, size_t payload_len, |
152 | size_t *out_len) | |
6fc6879b | 153 | { |
3bff59f8 | 154 | u8 *pos, *ct_start, *length, *cpayload; |
6fc6879b JM |
155 | struct crypto_hash *hmac; |
156 | size_t clen; | |
5c47af9a | 157 | int explicit_iv; |
6fc6879b JM |
158 | |
159 | pos = buf; | |
3bff59f8 JM |
160 | if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size) |
161 | return -1; | |
162 | ||
6fc6879b JM |
163 | /* ContentType type */ |
164 | ct_start = pos; | |
165 | *pos++ = content_type; | |
166 | /* ProtocolVersion version */ | |
5c47af9a | 167 | WPA_PUT_BE16(pos, rl->tls_version); |
6fc6879b JM |
168 | pos += 2; |
169 | /* uint16 length */ | |
170 | length = pos; | |
171 | WPA_PUT_BE16(length, payload_len); | |
172 | pos += 2; | |
173 | ||
3bff59f8 | 174 | cpayload = pos; |
5c47af9a | 175 | explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL && |
bcf03f52 | 176 | rl->iv_size && rl->tls_version >= TLS_VERSION_1_1; |
5c47af9a JM |
177 | if (explicit_iv) { |
178 | /* opaque IV[Cipherspec.block_length] */ | |
179 | if (pos + rl->iv_size > buf + buf_size) | |
180 | return -1; | |
181 | ||
182 | /* | |
183 | * Use random number R per the RFC 4346, 6.2.3.2 CBC Block | |
184 | * Cipher option 2a. | |
185 | */ | |
186 | ||
187 | if (os_get_random(pos, rl->iv_size)) | |
188 | return -1; | |
189 | pos += rl->iv_size; | |
190 | } | |
3bff59f8 JM |
191 | |
192 | /* | |
193 | * opaque fragment[TLSPlaintext.length] | |
194 | * (opaque content[TLSCompressed.length] in GenericBlockCipher) | |
195 | */ | |
196 | if (pos + payload_len > buf + buf_size) | |
197 | return -1; | |
198 | os_memmove(pos, payload, payload_len); | |
6fc6879b JM |
199 | pos += payload_len; |
200 | ||
201 | if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) { | |
3bff59f8 JM |
202 | /* |
203 | * MAC calculated over seq_num + TLSCompressed.type + | |
204 | * TLSCompressed.version + TLSCompressed.length + | |
205 | * TLSCompressed.fragment | |
206 | */ | |
6fc6879b JM |
207 | hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, |
208 | rl->hash_size); | |
209 | if (hmac == NULL) { | |
210 | wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " | |
211 | "to initialize HMAC"); | |
212 | return -1; | |
213 | } | |
214 | crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN); | |
215 | /* type + version + length + fragment */ | |
3bff59f8 JM |
216 | crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN); |
217 | crypto_hash_update(hmac, payload, payload_len); | |
6fc6879b JM |
218 | clen = buf + buf_size - pos; |
219 | if (clen < rl->hash_size) { | |
220 | wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " | |
221 | "enough room for MAC"); | |
222 | crypto_hash_finish(hmac, NULL, NULL); | |
223 | return -1; | |
224 | } | |
225 | ||
226 | if (crypto_hash_finish(hmac, pos, &clen) < 0) { | |
227 | wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " | |
228 | "to calculate HMAC"); | |
229 | return -1; | |
230 | } | |
231 | wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC", | |
232 | pos, clen); | |
233 | pos += clen; | |
234 | if (rl->iv_size) { | |
3bff59f8 | 235 | size_t len = pos - cpayload; |
6fc6879b JM |
236 | size_t pad; |
237 | pad = (len + 1) % rl->iv_size; | |
238 | if (pad) | |
239 | pad = rl->iv_size - pad; | |
240 | if (pos + pad + 1 > buf + buf_size) { | |
241 | wpa_printf(MSG_DEBUG, "TLSv1: No room for " | |
242 | "block cipher padding"); | |
243 | return -1; | |
244 | } | |
245 | os_memset(pos, pad, pad + 1); | |
246 | pos += pad + 1; | |
247 | } | |
248 | ||
3bff59f8 JM |
249 | if (crypto_cipher_encrypt(rl->write_cbc, cpayload, |
250 | cpayload, pos - cpayload) < 0) | |
6fc6879b JM |
251 | return -1; |
252 | } | |
253 | ||
254 | WPA_PUT_BE16(length, pos - length - 2); | |
255 | inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN); | |
256 | ||
257 | *out_len = pos - buf; | |
258 | ||
259 | return 0; | |
260 | } | |
261 | ||
262 | ||
263 | /** | |
264 | * tlsv1_record_receive - TLS record layer: Process a received message | |
265 | * @rl: Pointer to TLS record layer data | |
266 | * @in_data: Received data | |
267 | * @in_len: Length of the received data | |
268 | * @out_data: Buffer for output data (must be at least as long as in_data) | |
269 | * @out_len: Set to maximum out_data length by caller; used to return the | |
270 | * length of the used data | |
271 | * @alert: Buffer for returning an alert value on failure | |
c4a34808 JM |
272 | * Returns: Number of bytes used from in_data on success, 0 if record was not |
273 | * complete (more data needed), or -1 on failure | |
6fc6879b JM |
274 | * |
275 | * This function decrypts the received message, verifies HMAC and TLS record | |
276 | * layer header. | |
277 | */ | |
278 | int tlsv1_record_receive(struct tlsv1_record_layer *rl, | |
279 | const u8 *in_data, size_t in_len, | |
280 | u8 *out_data, size_t *out_len, u8 *alert) | |
281 | { | |
282 | size_t i, rlen, hlen; | |
283 | u8 padlen; | |
284 | struct crypto_hash *hmac; | |
285 | u8 len[2], hash[100]; | |
85b7187f | 286 | int force_mac_error = 0; |
c4a34808 | 287 | u8 ct; |
6fc6879b JM |
288 | |
289 | if (in_len < TLS_RECORD_HEADER_LEN) { | |
c4a34808 JM |
290 | wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - " |
291 | "need more data", | |
6fc6879b | 292 | (unsigned long) in_len); |
c4a34808 JM |
293 | wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", |
294 | in_data, in_len); | |
295 | return 0; | |
6fc6879b JM |
296 | } |
297 | ||
c4a34808 JM |
298 | ct = in_data[0]; |
299 | rlen = WPA_GET_BE16(in_data + 3); | |
6fc6879b | 300 | wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d " |
c4a34808 | 301 | "length %d", ct, in_data[1], in_data[2], (int) rlen); |
6fc6879b | 302 | |
61f1ed91 JM |
303 | /* |
304 | * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the | |
305 | * protocol version in record layer. As such, accept any {03,xx} value | |
306 | * to remain compatible with existing implementations. | |
307 | */ | |
308 | if (in_data[1] != 0x03) { | |
6fc6879b | 309 | wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version " |
61f1ed91 | 310 | "%u.%u", in_data[1], in_data[2]); |
6fc6879b JM |
311 | *alert = TLS_ALERT_PROTOCOL_VERSION; |
312 | return -1; | |
313 | } | |
314 | ||
6fc6879b JM |
315 | /* TLSCiphertext must not be more than 2^14+2048 bytes */ |
316 | if (TLS_RECORD_HEADER_LEN + rlen > 18432) { | |
317 | wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", | |
318 | (unsigned long) (TLS_RECORD_HEADER_LEN + rlen)); | |
319 | *alert = TLS_ALERT_RECORD_OVERFLOW; | |
320 | return -1; | |
321 | } | |
322 | ||
323 | in_data += TLS_RECORD_HEADER_LEN; | |
324 | in_len -= TLS_RECORD_HEADER_LEN; | |
325 | ||
326 | if (rlen > in_len) { | |
327 | wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included " | |
328 | "(rlen=%lu > in_len=%lu)", | |
329 | (unsigned long) rlen, (unsigned long) in_len); | |
c4a34808 JM |
330 | return 0; |
331 | } | |
332 | ||
333 | wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", | |
334 | in_data, rlen); | |
335 | ||
336 | if (ct != TLS_CONTENT_TYPE_HANDSHAKE && | |
337 | ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && | |
338 | ct != TLS_CONTENT_TYPE_ALERT && | |
339 | ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { | |
340 | wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown " | |
341 | "content type 0x%x", ct); | |
342 | *alert = TLS_ALERT_UNEXPECTED_MESSAGE; | |
6fc6879b JM |
343 | return -1; |
344 | } | |
345 | ||
346 | in_len = rlen; | |
347 | ||
348 | if (*out_len < in_len) { | |
349 | wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for " | |
350 | "processing received record"); | |
351 | *alert = TLS_ALERT_INTERNAL_ERROR; | |
352 | return -1; | |
353 | } | |
354 | ||
6fc6879b | 355 | if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) { |
26296a8a JM |
356 | size_t plen; |
357 | if (crypto_cipher_decrypt(rl->read_cbc, in_data, | |
6fc6879b JM |
358 | out_data, in_len) < 0) { |
359 | *alert = TLS_ALERT_DECRYPTION_FAILED; | |
360 | return -1; | |
361 | } | |
26296a8a | 362 | plen = in_len; |
5c47af9a JM |
363 | wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " |
364 | "data", out_data, plen); | |
365 | ||
6fc6879b | 366 | if (rl->iv_size) { |
85b7187f JM |
367 | /* |
368 | * TLS v1.0 defines different alert values for various | |
369 | * failures. That may information to aid in attacks, so | |
370 | * use the same bad_record_mac alert regardless of the | |
371 | * issues. | |
372 | * | |
373 | * In addition, instead of returning immediately on | |
374 | * error, run through the MAC check to make timing | |
375 | * attacks more difficult. | |
376 | */ | |
377 | ||
bcf03f52 | 378 | if (rl->tls_version >= TLS_VERSION_1_1) { |
5c47af9a JM |
379 | /* Remove opaque IV[Cipherspec.block_length] */ |
380 | if (plen < rl->iv_size) { | |
381 | wpa_printf(MSG_DEBUG, "TLSv1.1: Not " | |
382 | "enough room for IV"); | |
383 | force_mac_error = 1; | |
384 | goto check_mac; | |
385 | } | |
386 | os_memmove(out_data, out_data + rl->iv_size, | |
387 | plen - rl->iv_size); | |
388 | plen -= rl->iv_size; | |
389 | } | |
390 | ||
85b7187f | 391 | /* Verify and remove padding */ |
26296a8a | 392 | if (plen == 0) { |
6fc6879b JM |
393 | wpa_printf(MSG_DEBUG, "TLSv1: Too short record" |
394 | " (no pad)"); | |
85b7187f JM |
395 | force_mac_error = 1; |
396 | goto check_mac; | |
6fc6879b | 397 | } |
26296a8a JM |
398 | padlen = out_data[plen - 1]; |
399 | if (padlen >= plen) { | |
6fc6879b | 400 | wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad " |
26296a8a | 401 | "length (%u, plen=%lu) in " |
6fc6879b | 402 | "received record", |
26296a8a | 403 | padlen, (unsigned long) plen); |
85b7187f JM |
404 | force_mac_error = 1; |
405 | goto check_mac; | |
6fc6879b | 406 | } |
613522a4 | 407 | for (i = plen - padlen - 1; i < plen - 1; i++) { |
6fc6879b JM |
408 | if (out_data[i] != padlen) { |
409 | wpa_hexdump(MSG_DEBUG, | |
410 | "TLSv1: Invalid pad in " | |
411 | "received record", | |
613522a4 JM |
412 | out_data + plen - padlen - |
413 | 1, padlen + 1); | |
85b7187f JM |
414 | force_mac_error = 1; |
415 | goto check_mac; | |
6fc6879b JM |
416 | } |
417 | } | |
418 | ||
26296a8a | 419 | plen -= padlen + 1; |
c4a34808 JM |
420 | |
421 | wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - " | |
422 | "Decrypted data with IV and padding " | |
423 | "removed", out_data, plen); | |
6fc6879b JM |
424 | } |
425 | ||
85b7187f | 426 | check_mac: |
26296a8a | 427 | if (plen < rl->hash_size) { |
6fc6879b JM |
428 | wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " |
429 | "hash value"); | |
85b7187f | 430 | *alert = TLS_ALERT_BAD_RECORD_MAC; |
6fc6879b JM |
431 | return -1; |
432 | } | |
433 | ||
26296a8a | 434 | plen -= rl->hash_size; |
6fc6879b JM |
435 | |
436 | hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, | |
437 | rl->hash_size); | |
438 | if (hmac == NULL) { | |
439 | wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " | |
440 | "to initialize HMAC"); | |
441 | *alert = TLS_ALERT_INTERNAL_ERROR; | |
442 | return -1; | |
443 | } | |
444 | ||
445 | crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN); | |
446 | /* type + version + length + fragment */ | |
447 | crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3); | |
26296a8a | 448 | WPA_PUT_BE16(len, plen); |
6fc6879b | 449 | crypto_hash_update(hmac, len, 2); |
26296a8a | 450 | crypto_hash_update(hmac, out_data, plen); |
6fc6879b JM |
451 | hlen = sizeof(hash); |
452 | if (crypto_hash_finish(hmac, hash, &hlen) < 0) { | |
453 | wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " | |
454 | "to calculate HMAC"); | |
85b7187f | 455 | *alert = TLS_ALERT_INTERNAL_ERROR; |
6fc6879b JM |
456 | return -1; |
457 | } | |
458 | if (hlen != rl->hash_size || | |
85b7187f JM |
459 | os_memcmp(hash, out_data + plen, hlen) != 0 || |
460 | force_mac_error) { | |
6fc6879b | 461 | wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " |
85b7187f JM |
462 | "received message (force_mac_error=%d)", |
463 | force_mac_error); | |
6fc6879b JM |
464 | *alert = TLS_ALERT_BAD_RECORD_MAC; |
465 | return -1; | |
466 | } | |
26296a8a JM |
467 | |
468 | *out_len = plen; | |
469 | } else { | |
470 | os_memcpy(out_data, in_data, in_len); | |
471 | *out_len = in_len; | |
6fc6879b JM |
472 | } |
473 | ||
474 | /* TLSCompressed must not be more than 2^14+1024 bytes */ | |
475 | if (TLS_RECORD_HEADER_LEN + *out_len > 17408) { | |
476 | wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", | |
477 | (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len)); | |
478 | *alert = TLS_ALERT_RECORD_OVERFLOW; | |
479 | return -1; | |
480 | } | |
481 | ||
482 | inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN); | |
483 | ||
c4a34808 | 484 | return TLS_RECORD_HEADER_LEN + rlen; |
6fc6879b | 485 | } |