1189251Ssam/* 2189251Ssam * TLSv1 Record Protocol 3189251Ssam * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam */ 14189251Ssam 15189251Ssam#include "includes.h" 16189251Ssam 17189251Ssam#include "common.h" 18214734Srpaulo#include "crypto/md5.h" 19214734Srpaulo#include "crypto/sha1.h" 20189251Ssam#include "tlsv1_common.h" 21189251Ssam#include "tlsv1_record.h" 22189251Ssam 23189251Ssam 24189251Ssam/** 25189251Ssam * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite 26189251Ssam * @rl: Pointer to TLS record layer data 27189251Ssam * @cipher_suite: New cipher suite 28189251Ssam * Returns: 0 on success, -1 on failure 29189251Ssam * 30189251Ssam * This function is used to prepare TLS record layer for cipher suite change. 31189251Ssam * tlsv1_record_change_write_cipher() and 32189251Ssam * tlsv1_record_change_read_cipher() functions can then be used to change the 33189251Ssam * currently used ciphers. 34189251Ssam */ 35189251Ssamint tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, 36189251Ssam u16 cipher_suite) 37189251Ssam{ 38189251Ssam const struct tls_cipher_suite *suite; 39189251Ssam const struct tls_cipher_data *data; 40189251Ssam 41189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x", 42189251Ssam cipher_suite); 43189251Ssam rl->cipher_suite = cipher_suite; 44189251Ssam 45189251Ssam suite = tls_get_cipher_suite(cipher_suite); 46189251Ssam if (suite == NULL) 47189251Ssam return -1; 48189251Ssam 49189251Ssam if (suite->hash == TLS_HASH_MD5) { 50189251Ssam rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5; 51189251Ssam rl->hash_size = MD5_MAC_LEN; 52189251Ssam } else if (suite->hash == TLS_HASH_SHA) { 53189251Ssam rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1; 54189251Ssam rl->hash_size = SHA1_MAC_LEN; 55189251Ssam } 56189251Ssam 57189251Ssam data = tls_get_cipher_data(suite->cipher); 58189251Ssam if (data == NULL) 59189251Ssam return -1; 60189251Ssam 61189251Ssam rl->key_material_len = data->key_material; 62189251Ssam rl->iv_size = data->block_size; 63189251Ssam rl->cipher_alg = data->alg; 64189251Ssam 65189251Ssam return 0; 66189251Ssam} 67189251Ssam 68189251Ssam 69189251Ssam/** 70189251Ssam * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher 71189251Ssam * @rl: Pointer to TLS record layer data 72189251Ssam * Returns: 0 on success (cipher changed), -1 on failure 73189251Ssam * 74189251Ssam * This function changes TLS record layer to use the new cipher suite 75189251Ssam * configured with tlsv1_record_set_cipher_suite() for writing. 76189251Ssam */ 77189251Ssamint tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl) 78189251Ssam{ 79189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite " 80189251Ssam "0x%04x", rl->cipher_suite); 81189251Ssam rl->write_cipher_suite = rl->cipher_suite; 82189251Ssam os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN); 83189251Ssam 84189251Ssam if (rl->write_cbc) { 85189251Ssam crypto_cipher_deinit(rl->write_cbc); 86189251Ssam rl->write_cbc = NULL; 87189251Ssam } 88189251Ssam if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { 89189251Ssam rl->write_cbc = crypto_cipher_init(rl->cipher_alg, 90189251Ssam rl->write_iv, rl->write_key, 91189251Ssam rl->key_material_len); 92189251Ssam if (rl->write_cbc == NULL) { 93189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " 94189251Ssam "cipher"); 95189251Ssam return -1; 96189251Ssam } 97189251Ssam } 98189251Ssam 99189251Ssam return 0; 100189251Ssam} 101189251Ssam 102189251Ssam 103189251Ssam/** 104189251Ssam * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher 105189251Ssam * @rl: Pointer to TLS record layer data 106189251Ssam * Returns: 0 on success (cipher changed), -1 on failure 107189251Ssam * 108189251Ssam * This function changes TLS record layer to use the new cipher suite 109189251Ssam * configured with tlsv1_record_set_cipher_suite() for reading. 110189251Ssam */ 111189251Ssamint tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) 112189251Ssam{ 113189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite " 114189251Ssam "0x%04x", rl->cipher_suite); 115189251Ssam rl->read_cipher_suite = rl->cipher_suite; 116189251Ssam os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN); 117189251Ssam 118189251Ssam if (rl->read_cbc) { 119189251Ssam crypto_cipher_deinit(rl->read_cbc); 120189251Ssam rl->read_cbc = NULL; 121189251Ssam } 122189251Ssam if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { 123189251Ssam rl->read_cbc = crypto_cipher_init(rl->cipher_alg, 124189251Ssam rl->read_iv, rl->read_key, 125189251Ssam rl->key_material_len); 126189251Ssam if (rl->read_cbc == NULL) { 127189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " 128189251Ssam "cipher"); 129189251Ssam return -1; 130189251Ssam } 131189251Ssam } 132189251Ssam 133189251Ssam return 0; 134189251Ssam} 135189251Ssam 136189251Ssam 137189251Ssam/** 138189251Ssam * tlsv1_record_send - TLS record layer: Send a message 139189251Ssam * @rl: Pointer to TLS record layer data 140189251Ssam * @content_type: Content type (TLS_CONTENT_TYPE_*) 141189251Ssam * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the 142189251Ssam * beginning for record layer to fill in; payload filled in after this and 143189251Ssam * extra space in the end for HMAC). 144189251Ssam * @buf_size: Maximum buf size 145189251Ssam * @payload_len: Length of the payload 146189251Ssam * @out_len: Buffer for returning the used buf length 147189251Ssam * Returns: 0 on success, -1 on failure 148189251Ssam * 149189251Ssam * This function fills in the TLS record layer header, adds HMAC, and encrypts 150189251Ssam * the data using the current write cipher. 151189251Ssam */ 152189251Ssamint tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, 153189251Ssam size_t buf_size, size_t payload_len, size_t *out_len) 154189251Ssam{ 155189251Ssam u8 *pos, *ct_start, *length, *payload; 156189251Ssam struct crypto_hash *hmac; 157189251Ssam size_t clen; 158189251Ssam 159189251Ssam pos = buf; 160189251Ssam /* ContentType type */ 161189251Ssam ct_start = pos; 162189251Ssam *pos++ = content_type; 163189251Ssam /* ProtocolVersion version */ 164189251Ssam WPA_PUT_BE16(pos, TLS_VERSION); 165189251Ssam pos += 2; 166189251Ssam /* uint16 length */ 167189251Ssam length = pos; 168189251Ssam WPA_PUT_BE16(length, payload_len); 169189251Ssam pos += 2; 170189251Ssam 171189251Ssam /* opaque fragment[TLSPlaintext.length] */ 172189251Ssam payload = pos; 173189251Ssam pos += payload_len; 174189251Ssam 175189251Ssam if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) { 176189251Ssam hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, 177189251Ssam rl->hash_size); 178189251Ssam if (hmac == NULL) { 179189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " 180189251Ssam "to initialize HMAC"); 181189251Ssam return -1; 182189251Ssam } 183189251Ssam crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN); 184189251Ssam /* type + version + length + fragment */ 185189251Ssam crypto_hash_update(hmac, ct_start, pos - ct_start); 186189251Ssam clen = buf + buf_size - pos; 187189251Ssam if (clen < rl->hash_size) { 188189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " 189189251Ssam "enough room for MAC"); 190189251Ssam crypto_hash_finish(hmac, NULL, NULL); 191189251Ssam return -1; 192189251Ssam } 193189251Ssam 194189251Ssam if (crypto_hash_finish(hmac, pos, &clen) < 0) { 195189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " 196189251Ssam "to calculate HMAC"); 197189251Ssam return -1; 198189251Ssam } 199189251Ssam wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC", 200189251Ssam pos, clen); 201189251Ssam pos += clen; 202189251Ssam if (rl->iv_size) { 203189251Ssam size_t len = pos - payload; 204189251Ssam size_t pad; 205189251Ssam pad = (len + 1) % rl->iv_size; 206189251Ssam if (pad) 207189251Ssam pad = rl->iv_size - pad; 208189251Ssam if (pos + pad + 1 > buf + buf_size) { 209189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: No room for " 210189251Ssam "block cipher padding"); 211189251Ssam return -1; 212189251Ssam } 213189251Ssam os_memset(pos, pad, pad + 1); 214189251Ssam pos += pad + 1; 215189251Ssam } 216189251Ssam 217189251Ssam if (crypto_cipher_encrypt(rl->write_cbc, payload, 218189251Ssam payload, pos - payload) < 0) 219189251Ssam return -1; 220189251Ssam } 221189251Ssam 222189251Ssam WPA_PUT_BE16(length, pos - length - 2); 223189251Ssam inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN); 224189251Ssam 225189251Ssam *out_len = pos - buf; 226189251Ssam 227189251Ssam return 0; 228189251Ssam} 229189251Ssam 230189251Ssam 231189251Ssam/** 232189251Ssam * tlsv1_record_receive - TLS record layer: Process a received message 233189251Ssam * @rl: Pointer to TLS record layer data 234189251Ssam * @in_data: Received data 235189251Ssam * @in_len: Length of the received data 236189251Ssam * @out_data: Buffer for output data (must be at least as long as in_data) 237189251Ssam * @out_len: Set to maximum out_data length by caller; used to return the 238189251Ssam * length of the used data 239189251Ssam * @alert: Buffer for returning an alert value on failure 240189251Ssam * Returns: 0 on success, -1 on failure 241189251Ssam * 242189251Ssam * This function decrypts the received message, verifies HMAC and TLS record 243189251Ssam * layer header. 244189251Ssam */ 245189251Ssamint tlsv1_record_receive(struct tlsv1_record_layer *rl, 246189251Ssam const u8 *in_data, size_t in_len, 247189251Ssam u8 *out_data, size_t *out_len, u8 *alert) 248189251Ssam{ 249189251Ssam size_t i, rlen, hlen; 250189251Ssam u8 padlen; 251189251Ssam struct crypto_hash *hmac; 252189251Ssam u8 len[2], hash[100]; 253189251Ssam 254189251Ssam wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", 255189251Ssam in_data, in_len); 256189251Ssam 257189251Ssam if (in_len < TLS_RECORD_HEADER_LEN) { 258189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)", 259189251Ssam (unsigned long) in_len); 260189251Ssam *alert = TLS_ALERT_DECODE_ERROR; 261189251Ssam return -1; 262189251Ssam } 263189251Ssam 264189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d " 265189251Ssam "length %d", in_data[0], in_data[1], in_data[2], 266189251Ssam WPA_GET_BE16(in_data + 3)); 267189251Ssam 268189251Ssam if (in_data[0] != TLS_CONTENT_TYPE_HANDSHAKE && 269189251Ssam in_data[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && 270189251Ssam in_data[0] != TLS_CONTENT_TYPE_ALERT && 271189251Ssam in_data[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { 272189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type 0x%x", 273189251Ssam in_data[0]); 274189251Ssam *alert = TLS_ALERT_UNEXPECTED_MESSAGE; 275189251Ssam return -1; 276189251Ssam } 277189251Ssam 278189251Ssam if (WPA_GET_BE16(in_data + 1) != TLS_VERSION) { 279189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version " 280189251Ssam "%d.%d", in_data[1], in_data[2]); 281189251Ssam *alert = TLS_ALERT_PROTOCOL_VERSION; 282189251Ssam return -1; 283189251Ssam } 284189251Ssam 285189251Ssam rlen = WPA_GET_BE16(in_data + 3); 286189251Ssam 287189251Ssam /* TLSCiphertext must not be more than 2^14+2048 bytes */ 288189251Ssam if (TLS_RECORD_HEADER_LEN + rlen > 18432) { 289189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", 290189251Ssam (unsigned long) (TLS_RECORD_HEADER_LEN + rlen)); 291189251Ssam *alert = TLS_ALERT_RECORD_OVERFLOW; 292189251Ssam return -1; 293189251Ssam } 294189251Ssam 295189251Ssam in_data += TLS_RECORD_HEADER_LEN; 296189251Ssam in_len -= TLS_RECORD_HEADER_LEN; 297189251Ssam 298189251Ssam if (rlen > in_len) { 299189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included " 300189251Ssam "(rlen=%lu > in_len=%lu)", 301189251Ssam (unsigned long) rlen, (unsigned long) in_len); 302189251Ssam *alert = TLS_ALERT_DECODE_ERROR; 303189251Ssam return -1; 304189251Ssam } 305189251Ssam 306189251Ssam in_len = rlen; 307189251Ssam 308189251Ssam if (*out_len < in_len) { 309189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for " 310189251Ssam "processing received record"); 311189251Ssam *alert = TLS_ALERT_INTERNAL_ERROR; 312189251Ssam return -1; 313189251Ssam } 314189251Ssam 315189251Ssam os_memcpy(out_data, in_data, in_len); 316189251Ssam *out_len = in_len; 317189251Ssam 318189251Ssam if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) { 319189251Ssam if (crypto_cipher_decrypt(rl->read_cbc, out_data, 320189251Ssam out_data, in_len) < 0) { 321189251Ssam *alert = TLS_ALERT_DECRYPTION_FAILED; 322189251Ssam return -1; 323189251Ssam } 324189251Ssam if (rl->iv_size) { 325189251Ssam if (in_len == 0) { 326189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Too short record" 327189251Ssam " (no pad)"); 328189251Ssam *alert = TLS_ALERT_DECODE_ERROR; 329189251Ssam return -1; 330189251Ssam } 331189251Ssam padlen = out_data[in_len - 1]; 332189251Ssam if (padlen >= in_len) { 333189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad " 334189251Ssam "length (%u, in_len=%lu) in " 335189251Ssam "received record", 336189251Ssam padlen, (unsigned long) in_len); 337189251Ssam *alert = TLS_ALERT_DECRYPTION_FAILED; 338189251Ssam return -1; 339189251Ssam } 340189251Ssam for (i = in_len - padlen; i < in_len; i++) { 341189251Ssam if (out_data[i] != padlen) { 342189251Ssam wpa_hexdump(MSG_DEBUG, 343189251Ssam "TLSv1: Invalid pad in " 344189251Ssam "received record", 345189251Ssam out_data + in_len - padlen, 346189251Ssam padlen); 347189251Ssam *alert = TLS_ALERT_DECRYPTION_FAILED; 348189251Ssam return -1; 349189251Ssam } 350189251Ssam } 351189251Ssam 352189251Ssam *out_len -= padlen + 1; 353189251Ssam } 354189251Ssam 355189251Ssam wpa_hexdump(MSG_MSGDUMP, 356189251Ssam "TLSv1: Record Layer - Decrypted data", 357189251Ssam out_data, in_len); 358189251Ssam 359189251Ssam if (*out_len < rl->hash_size) { 360189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " 361189251Ssam "hash value"); 362189251Ssam *alert = TLS_ALERT_INTERNAL_ERROR; 363189251Ssam return -1; 364189251Ssam } 365189251Ssam 366189251Ssam *out_len -= rl->hash_size; 367189251Ssam 368189251Ssam hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, 369189251Ssam rl->hash_size); 370189251Ssam if (hmac == NULL) { 371189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " 372189251Ssam "to initialize HMAC"); 373189251Ssam *alert = TLS_ALERT_INTERNAL_ERROR; 374189251Ssam return -1; 375189251Ssam } 376189251Ssam 377189251Ssam crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN); 378189251Ssam /* type + version + length + fragment */ 379189251Ssam crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3); 380189251Ssam WPA_PUT_BE16(len, *out_len); 381189251Ssam crypto_hash_update(hmac, len, 2); 382189251Ssam crypto_hash_update(hmac, out_data, *out_len); 383189251Ssam hlen = sizeof(hash); 384189251Ssam if (crypto_hash_finish(hmac, hash, &hlen) < 0) { 385189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " 386189251Ssam "to calculate HMAC"); 387189251Ssam return -1; 388189251Ssam } 389189251Ssam if (hlen != rl->hash_size || 390189251Ssam os_memcmp(hash, out_data + *out_len, hlen) != 0) { 391189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " 392189251Ssam "received message"); 393189251Ssam *alert = TLS_ALERT_BAD_RECORD_MAC; 394189251Ssam return -1; 395189251Ssam } 396189251Ssam } 397189251Ssam 398189251Ssam /* TLSCompressed must not be more than 2^14+1024 bytes */ 399189251Ssam if (TLS_RECORD_HEADER_LEN + *out_len > 17408) { 400189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", 401189251Ssam (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len)); 402189251Ssam *alert = TLS_ALERT_RECORD_OVERFLOW; 403189251Ssam return -1; 404189251Ssam } 405189251Ssam 406189251Ssam inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN); 407189251Ssam 408189251Ssam return 0; 409189251Ssam} 410