Merge branch 'vendor/NCURSES'
[dragonfly.git] / contrib / hostapd / src / tls / tlsv1_record.c
1 /*
2  * TLSv1 Record Protocol
3  * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
4  *
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.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16
17 #include "common.h"
18 #include "md5.h"
19 #include "sha1.h"
20 #include "tlsv1_common.h"
21 #include "tlsv1_record.h"
22
23
24 /**
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
29  *
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.
34  */
35 int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
36                                   u16 cipher_suite)
37 {
38         const struct tls_cipher_suite *suite;
39         const struct tls_cipher_data *data;
40
41         wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x",
42                    cipher_suite);
43         rl->cipher_suite = cipher_suite;
44
45         suite = tls_get_cipher_suite(cipher_suite);
46         if (suite == NULL)
47                 return -1;
48
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;
55         }
56
57         data = tls_get_cipher_data(suite->cipher);
58         if (data == NULL)
59                 return -1;
60
61         rl->key_material_len = data->key_material;
62         rl->iv_size = data->block_size;
63         rl->cipher_alg = data->alg;
64
65         return 0;
66 }
67
68
69 /**
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
73  *
74  * This function changes TLS record layer to use the new cipher suite
75  * configured with tlsv1_record_set_cipher_suite() for writing.
76  */
77 int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl)
78 {
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);
83
84         if (rl->write_cbc) {
85                 crypto_cipher_deinit(rl->write_cbc);
86                 rl->write_cbc = NULL;
87         }
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 "
94                                    "cipher");
95                         return -1;
96                 }
97         }
98
99         return 0;
100 }
101
102
103 /**
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
107  *
108  * This function changes TLS record layer to use the new cipher suite
109  * configured with tlsv1_record_set_cipher_suite() for reading.
110  */
111 int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
112 {
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);
117
118         if (rl->read_cbc) {
119                 crypto_cipher_deinit(rl->read_cbc);
120                 rl->read_cbc = NULL;
121         }
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 "
128                                    "cipher");
129                         return -1;
130                 }
131         }
132
133         return 0;
134 }
135
136
137 /**
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
148  *
149  * This function fills in the TLS record layer header, adds HMAC, and encrypts
150  * the data using the current write cipher.
151  */
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)
154 {
155         u8 *pos, *ct_start, *length, *payload;
156         struct crypto_hash *hmac;
157         size_t clen;
158
159         pos = buf;
160         /* ContentType type */
161         ct_start = pos;
162         *pos++ = content_type;
163         /* ProtocolVersion version */
164         WPA_PUT_BE16(pos, TLS_VERSION);
165         pos += 2;
166         /* uint16 length */
167         length = pos;
168         WPA_PUT_BE16(length, payload_len);
169         pos += 2;
170
171         /* opaque fragment[TLSPlaintext.length] */
172         payload = pos;
173         pos += payload_len;
174
175         if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
176                 hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret,
177                                         rl->hash_size);
178                 if (hmac == NULL) {
179                         wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
180                                    "to initialize HMAC");
181                         return -1;
182                 }
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);
191                         return -1;
192                 }
193
194                 if (crypto_hash_finish(hmac, pos, &clen) < 0) {
195                         wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
196                                    "to calculate HMAC");
197                         return -1;
198                 }
199                 wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC",
200                             pos, clen);
201                 pos += clen;
202                 if (rl->iv_size) {
203                         size_t len = pos - payload;
204                         size_t pad;
205                         pad = (len + 1) % rl->iv_size;
206                         if (pad)
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");
211                                 return -1;
212                         }
213                         os_memset(pos, pad, pad + 1);
214                         pos += pad + 1;
215                 }
216
217                 if (crypto_cipher_encrypt(rl->write_cbc, payload,
218                                           payload, pos - payload) < 0)
219                         return -1;
220         }
221
222         WPA_PUT_BE16(length, pos - length - 2);
223         inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN);
224
225         *out_len = pos - buf;
226
227         return 0;
228 }
229
230
231 /**
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
241  *
242  * This function decrypts the received message, verifies HMAC and TLS record
243  * layer header.
244  */
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)
248 {
249         size_t i, rlen, hlen;
250         u8 padlen;
251         struct crypto_hash *hmac;
252         u8 len[2], hash[100];
253
254         wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
255                     in_data, in_len);
256
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;
261                 return -1;
262         }
263
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));
267
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",
273                            in_data[0]);
274                 *alert = TLS_ALERT_UNEXPECTED_MESSAGE;
275                 return -1;
276         }
277
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;
282                 return -1;
283         }
284
285         rlen = WPA_GET_BE16(in_data + 3);
286
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;
292                 return -1;
293         }
294
295         in_data += TLS_RECORD_HEADER_LEN;
296         in_len -= TLS_RECORD_HEADER_LEN;
297
298         if (rlen > in_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;
303                 return -1;
304         }
305
306         in_len = rlen;
307
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;
312                 return -1;
313         }
314
315         os_memcpy(out_data, in_data, in_len);
316         *out_len = in_len;
317
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;
322                         return -1;
323                 }
324                 if (rl->iv_size) {
325                         if (in_len == 0) {
326                                 wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
327                                            " (no pad)");
328                                 *alert = TLS_ALERT_DECODE_ERROR;
329                                 return -1;
330                         }
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 "
335                                            "received record",
336                                            padlen, (unsigned long) in_len);
337                                 *alert = TLS_ALERT_DECRYPTION_FAILED;
338                                 return -1;
339                         }
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 "
344                                                     "received record",
345                                                     out_data + in_len - padlen,
346                                                     padlen);
347                                         *alert = TLS_ALERT_DECRYPTION_FAILED;
348                                         return -1;
349                                 }
350                         }
351
352                         *out_len -= padlen + 1;
353                 }
354
355                 wpa_hexdump(MSG_MSGDUMP,
356                             "TLSv1: Record Layer - Decrypted data",
357                             out_data, in_len);
358
359                 if (*out_len < rl->hash_size) {
360                         wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
361                                    "hash value");
362                         *alert = TLS_ALERT_INTERNAL_ERROR;
363                         return -1;
364                 }
365
366                 *out_len -= rl->hash_size;
367
368                 hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret,
369                                         rl->hash_size);
370                 if (hmac == NULL) {
371                         wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
372                                    "to initialize HMAC");
373                         *alert = TLS_ALERT_INTERNAL_ERROR;
374                         return -1;
375                 }
376
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);
383                 hlen = sizeof(hash);
384                 if (crypto_hash_finish(hmac, hash, &hlen) < 0) {
385                         wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
386                                    "to calculate HMAC");
387                         return -1;
388                 }
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 "
392                                    "received message");
393                         *alert = TLS_ALERT_BAD_RECORD_MAC;
394                         return -1;
395                 }
396         }
397
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;
403                 return -1;
404         }
405
406         inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
407
408         return 0;
409 }