2 * TLSv1 Record Protocol
3 * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
20 #include "tlsv1_common.h"
21 #include "tlsv1_record.h"
25 * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite
26 * @rl: Pointer to TLS record layer data
27 * @cipher_suite: New cipher suite
28 * Returns: 0 on success, -1 on failure
30 * This function is used to prepare TLS record layer for cipher suite change.
31 * tlsv1_record_change_write_cipher() and
32 * tlsv1_record_change_read_cipher() functions can then be used to change the
33 * currently used ciphers.
35 int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer
*rl
,
38 const struct tls_cipher_suite
*suite
;
39 const struct tls_cipher_data
*data
;
41 wpa_printf(MSG_DEBUG
, "TLSv1: Selected cipher suite: 0x%04x",
43 rl
->cipher_suite
= cipher_suite
;
45 suite
= tls_get_cipher_suite(cipher_suite
);
49 if (suite
->hash
== TLS_HASH_MD5
) {
50 rl
->hash_alg
= CRYPTO_HASH_ALG_HMAC_MD5
;
51 rl
->hash_size
= MD5_MAC_LEN
;
52 } else if (suite
->hash
== TLS_HASH_SHA
) {
53 rl
->hash_alg
= CRYPTO_HASH_ALG_HMAC_SHA1
;
54 rl
->hash_size
= SHA1_MAC_LEN
;
57 data
= tls_get_cipher_data(suite
->cipher
);
61 rl
->key_material_len
= data
->key_material
;
62 rl
->iv_size
= data
->block_size
;
63 rl
->cipher_alg
= data
->alg
;
70 * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher
71 * @rl: Pointer to TLS record layer data
72 * Returns: 0 on success (cipher changed), -1 on failure
74 * This function changes TLS record layer to use the new cipher suite
75 * configured with tlsv1_record_set_cipher_suite() for writing.
77 int tlsv1_record_change_write_cipher(struct tlsv1_record_layer
*rl
)
79 wpa_printf(MSG_DEBUG
, "TLSv1: Record Layer - New write cipher suite "
80 "0x%04x", rl
->cipher_suite
);
81 rl
->write_cipher_suite
= rl
->cipher_suite
;
82 os_memset(rl
->write_seq_num
, 0, TLS_SEQ_NUM_LEN
);
85 crypto_cipher_deinit(rl
->write_cbc
);
88 if (rl
->cipher_alg
!= CRYPTO_CIPHER_NULL
) {
89 rl
->write_cbc
= crypto_cipher_init(rl
->cipher_alg
,
90 rl
->write_iv
, rl
->write_key
,
91 rl
->key_material_len
);
92 if (rl
->write_cbc
== NULL
) {
93 wpa_printf(MSG_DEBUG
, "TLSv1: Failed to initialize "
104 * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher
105 * @rl: Pointer to TLS record layer data
106 * Returns: 0 on success (cipher changed), -1 on failure
108 * This function changes TLS record layer to use the new cipher suite
109 * configured with tlsv1_record_set_cipher_suite() for reading.
111 int tlsv1_record_change_read_cipher(struct tlsv1_record_layer
*rl
)
113 wpa_printf(MSG_DEBUG
, "TLSv1: Record Layer - New read cipher suite "
114 "0x%04x", rl
->cipher_suite
);
115 rl
->read_cipher_suite
= rl
->cipher_suite
;
116 os_memset(rl
->read_seq_num
, 0, TLS_SEQ_NUM_LEN
);
119 crypto_cipher_deinit(rl
->read_cbc
);
122 if (rl
->cipher_alg
!= CRYPTO_CIPHER_NULL
) {
123 rl
->read_cbc
= crypto_cipher_init(rl
->cipher_alg
,
124 rl
->read_iv
, rl
->read_key
,
125 rl
->key_material_len
);
126 if (rl
->read_cbc
== NULL
) {
127 wpa_printf(MSG_DEBUG
, "TLSv1: Failed to initialize "
138 * tlsv1_record_send - TLS record layer: Send a message
139 * @rl: Pointer to TLS record layer data
140 * @content_type: Content type (TLS_CONTENT_TYPE_*)
141 * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the
142 * beginning for record layer to fill in; payload filled in after this and
143 * extra space in the end for HMAC).
144 * @buf_size: Maximum buf size
145 * @payload_len: Length of the payload
146 * @out_len: Buffer for returning the used buf length
147 * Returns: 0 on success, -1 on failure
149 * This function fills in the TLS record layer header, adds HMAC, and encrypts
150 * the data using the current write cipher.
152 int tlsv1_record_send(struct tlsv1_record_layer
*rl
, u8 content_type
, u8
*buf
,
153 size_t buf_size
, size_t payload_len
, size_t *out_len
)
155 u8
*pos
, *ct_start
, *length
, *payload
;
156 struct crypto_hash
*hmac
;
160 /* ContentType type */
162 *pos
++ = content_type
;
163 /* ProtocolVersion version */
164 WPA_PUT_BE16(pos
, TLS_VERSION
);
168 WPA_PUT_BE16(length
, payload_len
);
171 /* opaque fragment[TLSPlaintext.length] */
175 if (rl
->write_cipher_suite
!= TLS_NULL_WITH_NULL_NULL
) {
176 hmac
= crypto_hash_init(rl
->hash_alg
, rl
->write_mac_secret
,
179 wpa_printf(MSG_DEBUG
, "TLSv1: Record Layer - Failed "
180 "to initialize HMAC");
183 crypto_hash_update(hmac
, rl
->write_seq_num
, TLS_SEQ_NUM_LEN
);
184 /* type + version + length + fragment */
185 crypto_hash_update(hmac
, ct_start
, pos
- ct_start
);
186 clen
= buf
+ buf_size
- pos
;
187 if (clen
< rl
->hash_size
) {
188 wpa_printf(MSG_DEBUG
, "TLSv1: Record Layer - Not "
189 "enough room for MAC");
190 crypto_hash_finish(hmac
, NULL
, NULL
);
194 if (crypto_hash_finish(hmac
, pos
, &clen
) < 0) {
195 wpa_printf(MSG_DEBUG
, "TLSv1: Record Layer - Failed "
196 "to calculate HMAC");
199 wpa_hexdump(MSG_MSGDUMP
, "TLSv1: Record Layer - Write HMAC",
203 size_t len
= pos
- payload
;
205 pad
= (len
+ 1) % rl
->iv_size
;
207 pad
= rl
->iv_size
- pad
;
208 if (pos
+ pad
+ 1 > buf
+ buf_size
) {
209 wpa_printf(MSG_DEBUG
, "TLSv1: No room for "
210 "block cipher padding");
213 os_memset(pos
, pad
, pad
+ 1);
217 if (crypto_cipher_encrypt(rl
->write_cbc
, payload
,
218 payload
, pos
- payload
) < 0)
222 WPA_PUT_BE16(length
, pos
- length
- 2);
223 inc_byte_array(rl
->write_seq_num
, TLS_SEQ_NUM_LEN
);
225 *out_len
= pos
- buf
;
232 * tlsv1_record_receive - TLS record layer: Process a received message
233 * @rl: Pointer to TLS record layer data
234 * @in_data: Received data
235 * @in_len: Length of the received data
236 * @out_data: Buffer for output data (must be at least as long as in_data)
237 * @out_len: Set to maximum out_data length by caller; used to return the
238 * length of the used data
239 * @alert: Buffer for returning an alert value on failure
240 * Returns: 0 on success, -1 on failure
242 * This function decrypts the received message, verifies HMAC and TLS record
245 int tlsv1_record_receive(struct tlsv1_record_layer
*rl
,
246 const u8
*in_data
, size_t in_len
,
247 u8
*out_data
, size_t *out_len
, u8
*alert
)
249 size_t i
, rlen
, hlen
;
251 struct crypto_hash
*hmac
;
252 u8 len
[2], hash
[100];
254 wpa_hexdump(MSG_MSGDUMP
, "TLSv1: Record Layer - Received",
257 if (in_len
< TLS_RECORD_HEADER_LEN
) {
258 wpa_printf(MSG_DEBUG
, "TLSv1: Too short record (in_len=%lu)",
259 (unsigned long) in_len
);
260 *alert
= TLS_ALERT_DECODE_ERROR
;
264 wpa_printf(MSG_DEBUG
, "TLSv1: Received content type %d version %d.%d "
265 "length %d", in_data
[0], in_data
[1], in_data
[2],
266 WPA_GET_BE16(in_data
+ 3));
268 if (in_data
[0] != TLS_CONTENT_TYPE_HANDSHAKE
&&
269 in_data
[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC
&&
270 in_data
[0] != TLS_CONTENT_TYPE_ALERT
&&
271 in_data
[0] != TLS_CONTENT_TYPE_APPLICATION_DATA
) {
272 wpa_printf(MSG_DEBUG
, "TLSv1: Unexpected content type 0x%x",
274 *alert
= TLS_ALERT_UNEXPECTED_MESSAGE
;
278 if (WPA_GET_BE16(in_data
+ 1) != TLS_VERSION
) {
279 wpa_printf(MSG_DEBUG
, "TLSv1: Unexpected protocol version "
280 "%d.%d", in_data
[1], in_data
[2]);
281 *alert
= TLS_ALERT_PROTOCOL_VERSION
;
285 rlen
= WPA_GET_BE16(in_data
+ 3);
287 /* TLSCiphertext must not be more than 2^14+2048 bytes */
288 if (TLS_RECORD_HEADER_LEN
+ rlen
> 18432) {
289 wpa_printf(MSG_DEBUG
, "TLSv1: Record overflow (len=%lu)",
290 (unsigned long) (TLS_RECORD_HEADER_LEN
+ rlen
));
291 *alert
= TLS_ALERT_RECORD_OVERFLOW
;
295 in_data
+= TLS_RECORD_HEADER_LEN
;
296 in_len
-= TLS_RECORD_HEADER_LEN
;
299 wpa_printf(MSG_DEBUG
, "TLSv1: Not all record data included "
300 "(rlen=%lu > in_len=%lu)",
301 (unsigned long) rlen
, (unsigned long) in_len
);
302 *alert
= TLS_ALERT_DECODE_ERROR
;
308 if (*out_len
< in_len
) {
309 wpa_printf(MSG_DEBUG
, "TLSv1: Not enough output buffer for "
310 "processing received record");
311 *alert
= TLS_ALERT_INTERNAL_ERROR
;
315 os_memcpy(out_data
, in_data
, in_len
);
318 if (rl
->read_cipher_suite
!= TLS_NULL_WITH_NULL_NULL
) {
319 if (crypto_cipher_decrypt(rl
->read_cbc
, out_data
,
320 out_data
, in_len
) < 0) {
321 *alert
= TLS_ALERT_DECRYPTION_FAILED
;
326 wpa_printf(MSG_DEBUG
, "TLSv1: Too short record"
328 *alert
= TLS_ALERT_DECODE_ERROR
;
331 padlen
= out_data
[in_len
- 1];
332 if (padlen
>= in_len
) {
333 wpa_printf(MSG_DEBUG
, "TLSv1: Incorrect pad "
334 "length (%u, in_len=%lu) in "
336 padlen
, (unsigned long) in_len
);
337 *alert
= TLS_ALERT_DECRYPTION_FAILED
;
340 for (i
= in_len
- padlen
; i
< in_len
; i
++) {
341 if (out_data
[i
] != padlen
) {
342 wpa_hexdump(MSG_DEBUG
,
343 "TLSv1: Invalid pad in "
345 out_data
+ in_len
- padlen
,
347 *alert
= TLS_ALERT_DECRYPTION_FAILED
;
352 *out_len
-= padlen
+ 1;
355 wpa_hexdump(MSG_MSGDUMP
,
356 "TLSv1: Record Layer - Decrypted data",
359 if (*out_len
< rl
->hash_size
) {
360 wpa_printf(MSG_DEBUG
, "TLSv1: Too short record; no "
362 *alert
= TLS_ALERT_INTERNAL_ERROR
;
366 *out_len
-= rl
->hash_size
;
368 hmac
= crypto_hash_init(rl
->hash_alg
, rl
->read_mac_secret
,
371 wpa_printf(MSG_DEBUG
, "TLSv1: Record Layer - Failed "
372 "to initialize HMAC");
373 *alert
= TLS_ALERT_INTERNAL_ERROR
;
377 crypto_hash_update(hmac
, rl
->read_seq_num
, TLS_SEQ_NUM_LEN
);
378 /* type + version + length + fragment */
379 crypto_hash_update(hmac
, in_data
- TLS_RECORD_HEADER_LEN
, 3);
380 WPA_PUT_BE16(len
, *out_len
);
381 crypto_hash_update(hmac
, len
, 2);
382 crypto_hash_update(hmac
, out_data
, *out_len
);
384 if (crypto_hash_finish(hmac
, hash
, &hlen
) < 0) {
385 wpa_printf(MSG_DEBUG
, "TLSv1: Record Layer - Failed "
386 "to calculate HMAC");
389 if (hlen
!= rl
->hash_size
||
390 os_memcmp(hash
, out_data
+ *out_len
, hlen
) != 0) {
391 wpa_printf(MSG_DEBUG
, "TLSv1: Invalid HMAC value in "
393 *alert
= TLS_ALERT_BAD_RECORD_MAC
;
398 /* TLSCompressed must not be more than 2^14+1024 bytes */
399 if (TLS_RECORD_HEADER_LEN
+ *out_len
> 17408) {
400 wpa_printf(MSG_DEBUG
, "TLSv1: Record overflow (len=%lu)",
401 (unsigned long) (TLS_RECORD_HEADER_LEN
+ *out_len
));
402 *alert
= TLS_ALERT_RECORD_OVERFLOW
;
406 inc_byte_array(rl
->read_seq_num
, TLS_SEQ_NUM_LEN
);