text: /*
   *  Embedded Linux library
   *
   *  Copyright (C) 2015  Intel Corporation. All rights reserved.
   *
   *  This library is free software; you can redistribute it and/or
   *  modify it under the terms of the GNU Lesser General Public
   *  License as published by the Free Software Foundation; either
   *  version 2.1 of the License, or (at your option) any later version.
   *
   *  This library is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   *  Lesser General Public License for more details.
   *
   *  You should have received a copy of the GNU Lesser General Public
   *  License along with this library; if not, write to the Free Software
   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
   */
  
  #ifdef HAVE_CONFIG_H
  #include <config.h>
  #endif
  
  #define _GNU_SOURCE
  #include <alloca.h>
  
  #include "private.h"
  #include "tls.h"
  #include "checksum.h"
  #include "cipher.h"
  #include "cert.h"
  #include "tls-private.h"
  #include "random.h"
  
  /* Implementation-specific max Record Layer fragment size (must be < 16kB) */
  #define TX_RECORD_MAX_LEN	4096
  
  /* TLSPlaintext + TLSCompressed + TLSCiphertext headers + seq_num sizes */
  #define TX_RECORD_MAX_HEADERS	(5 + 5 + 8 + 5)
  #define TX_RECORD_MAX_MAC	64
  
  /* Head room and tail room for the buffer passed to the cipher */
  #define TX_RECORD_HEADROOM	TX_RECORD_MAX_HEADERS
  #define TX_RECORD_TAILROOM	TX_RECORD_MAX_MAC
  
  static void tls_write_mac(struct l_tls *tls, uint8_t *compressed,
  				uint16_t compressed_len, uint8_t *out_buf,
  				bool txrx)
  {
  	uint8_t *in_buf;
  
  	/* Prepend the sequence number to the TLSCompressed buffer */
  	in_buf = compressed - 8;
  	l_put_be64(tls->seq_num[txrx]++, in_buf);
  
  	if (tls->mac[txrx]) {
  		l_checksum_reset(tls->mac[txrx]);
  		l_checksum_update(tls->mac[txrx], in_buf, compressed_len + 8);
  		l_checksum_get_digest(tls->mac[txrx], out_buf,
  					tls->mac_length[txrx]);
  	}
  }
  
  static void tls_tx_record_plaintext(struct l_tls *tls,
  					uint8_t *plaintext,
  					uint16_t plaintext_len)
  {
  	uint8_t *compressed;
  	uint16_t compressed_len;
  	uint8_t *cipher_input;
  	uint16_t cipher_input_len;
  	uint8_t *ciphertext;
  	uint16_t ciphertext_len;
  	uint8_t padding_length;
  	uint8_t buf[TX_RECORD_HEADROOM + TX_RECORD_MAX_LEN +
  				TX_RECORD_TAILROOM];
  	uint8_t iv[32];
  	uint8_t *assocdata;
  	int offset;
  
  	/*
  	 * TODO: if DEFLATE is selected in current state, use a new buffer
  	 * on stack to write a TLSCompressed structure there, otherwise use
  	 * the provided buffer.  Since only null compression is supported
  	 * today we always use the provided buffer.
  	 */
  	compressed_len = plaintext_len - 5;
  	compressed = plaintext;
  
  	/* Build a TLSCompressed struct */
  	compressed[0] = plaintext[0]; /* Copy type and version fields */
  	compressed[1] = plaintext[1];
  	compressed[2] = plaintext[2];
  	compressed[3] = compressed_len >> 8;
  	compressed[4] = compressed_len >> 0;
  
  	switch (tls->cipher_type[1]) {
  	case TLS_CIPHER_STREAM:
  		/* Append the MAC after TLSCompressed.fragment, if needed */
  		tls_write_mac(tls, compressed, compressed_len + 5,
  				compressed + compressed_len + 5, true);
  
  		cipher_input = compressed + 5;
  		cipher_input_len = compressed_len + tls->mac_length[1];
  
  		if (!tls->cipher[1]) {
  			ciphertext = cipher_input;
  			ciphertext_len = cipher_input_len;
  		} else {
  			ciphertext = buf + TX_RECORD_HEADROOM;
  			ciphertext_len = cipher_input_len;
  			l_cipher_encrypt(tls->cipher[1], cipher_input,
  						ciphertext, cipher_input_len);
  		}
  
  		break;
  
  	case TLS_CIPHER_BLOCK:
  		/* Append the MAC after TLSCompressed.fragment, if needed */
  		cipher_input = compressed + 5;
  		tls_write_mac(tls, compressed, compressed_len + 5,
  				cipher_input + compressed_len, true);
  		cipher_input_len = compressed_len + tls->mac_length[1];
  
  		/* Add minimum padding */
  		padding_length = (~cipher_input_len) &
  			(tls->block_length[1] - 1);
  		memset(cipher_input + cipher_input_len, padding_length,
  				padding_length + 1);
  		cipher_input_len += padding_length + 1;
  
  		/* Generate an IV */
  		ciphertext = buf + TX_RECORD_HEADROOM;
  
  		offset = 0;
  
  		if (tls->negotiated_version >= L_TLS_V12) {
  			l_getrandom(ciphertext, tls->record_iv_length[1]);
  
  			l_cipher_set_iv(tls->cipher[1], ciphertext,
  					tls->record_iv_length[1]);
  
  			offset = tls->record_iv_length[1];
  		} else if (tls->negotiated_version >= L_TLS_V11) {
  			l_getrandom(iv, tls->record_iv_length[1]);
  
  			l_cipher_encrypt(tls->cipher[1], iv, ciphertext,
  						tls->record_iv_length[1]);
  
  			offset = tls->record_iv_length[1];
  		}
  
  		l_cipher_encrypt(tls->cipher[1], cipher_input,
  					ciphertext + offset, cipher_input_len);
  		ciphertext_len = offset + cipher_input_len;
  
  		break;
  
  	case TLS_CIPHER_AEAD:
  		/* Prepend seq_num to TLSCompressed.type + .version + .length */
  		assocdata = compressed - 8;
  		l_put_be64(tls->seq_num[1]++, assocdata);
  
  		cipher_input = compressed + 5;
  		cipher_input_len = compressed_len;
  
  		/*
  		 * Build the IV.  The explicit part generation method is
  		 * actually cipher suite-specific but our only AEAD cipher
  		 * suites only require this part to be unique for each
  		 * record.  For future suites there may need to be a callback
  		 * that generates the per-record IV or an enum for the suite
  		 * to select one of a few IV types.
  		 *
  		 * Note kernel's rfc4106(gcm(...)) algorithm could potentially
  		 * be used to build the IV.
  		 */
  		memcpy(iv, tls->fixed_iv[1], tls->fixed_iv_length[1]);
  		l_put_le64(tls->seq_num[1], iv + tls->fixed_iv_length[1]);
  
  		if (tls->record_iv_length[1] > 8)
  			memset(iv + tls->fixed_iv_length[1] + 8, 42,
  				tls->record_iv_length[1] - 8);
  
  		/* Build the GenericAEADCipher struct */
  		ciphertext = buf + TX_RECORD_HEADROOM;
  		memcpy(ciphertext, iv + tls->fixed_iv_length[1],
  			tls->record_iv_length[1]);
  		l_aead_cipher_encrypt(tls->aead_cipher[1],
  					cipher_input, cipher_input_len,
  					assocdata, 13,
  					iv, tls->fixed_iv_length[1] +
  					tls->record_iv_length[1],
  					ciphertext + tls->record_iv_length[1],
  					cipher_input_len +
  					tls->auth_tag_length[1]);
  
  		ciphertext_len = tls->record_iv_length[1] +
  			cipher_input_len + tls->auth_tag_length[1];
  
  		break;
  
  	default:
  		return;
  	}
  
  	/* Build a TLSCiphertext struct */
  	ciphertext -= 5;
  	ciphertext[0] = plaintext[0]; /* Copy type and version fields */
  	ciphertext[1] = plaintext[1];
  	ciphertext[2] = plaintext[2];
  	ciphertext[3] = ciphertext_len >> 8;
  	ciphertext[4] = ciphertext_len >> 0;
  
  	tls->tx(ciphertext, ciphertext_len + 5, tls->user_data);
  }
  
  void tls_tx_record(struct l_tls *tls, enum tls_content_type type,
  			const uint8_t *data, size_t len)
  {
  	uint8_t buf[TX_RECORD_HEADROOM + TX_RECORD_MAX_LEN +
  				TX_RECORD_TAILROOM];
  	uint8_t *fragment, *plaintext;
  	uint16_t fragment_len;
  	uint16_t version = tls->negotiated_version ?: tls->min_version;
  
  	if (type == TLS_CT_ALERT)
  		tls->record_flush = true;
  
  	while (len) {
  		fragment = buf + TX_RECORD_HEADROOM;
  		fragment_len = len < TX_RECORD_MAX_LEN ?
  			len : TX_RECORD_MAX_LEN;
  
  		/* Build a TLSPlaintext struct */
  		plaintext = fragment - 5;
  		plaintext[0] = type;
  		plaintext[1] = (uint8_t) (version >> 8);
  		plaintext[2] = (uint8_t) (version >> 0);
  		plaintext[3] = fragment_len >> 8;
  		plaintext[4] = fragment_len >> 0;
  		memcpy(plaintext + 5, data, fragment_len);
  
  		tls_tx_record_plaintext(tls, plaintext, fragment_len + 5);
  
  		data += fragment_len;
  		len -= fragment_len;
  	}
  }
  
  static bool tls_handle_plaintext(struct l_tls *tls, const uint8_t *plaintext,
  					int len, uint8_t type, uint16_t version)
  {
  	if (len > (1 << 14)) {
  		TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
  				"Plaintext message too long: %i", len);
  		return false;
  	}
  
  	switch (type) {
  	case TLS_CT_CHANGE_CIPHER_SPEC:
  	case TLS_CT_APPLICATION_DATA:
  		return tls_handle_message(tls, plaintext, len, type, version);
  
  	/*
  	 * We need to perform input reassembly twice at different levels:
  	 * once to make sure we're handling complete TLSCiphertext messages,
  	 * in l_tls_handle_rx(), and again here so that the Alert and
  	 * Handshake message type handlers deal with complete messages.
  	 * This does not affect ChangeCipherSpec messages because they're
  	 * just a single byte and there are never more than one such message
  	 * in a row.  Similarly it doesn't affect application data because
  	 * the application is not guaranteed that message boundaries are
  	 * preserved in any way and we don't know its message lengths anyway.
  	 * It does affect Alert because these messages are 2 byte long and
  	 * could potentially be split over two TLSPlaintext messages but
  	 * there are never more than one Alert in a TLSPlaintext for the same
  	 * reason as with ChangeCipherSpec.  Handshake messages are the
  	 * most affected although the need to do the reassembly twice still
  	 * seems wasteful considering most of these messages are sent in
  	 * plaintext and TLSCiphertext maps to TLSPlaintext records.
  	 */
  	case TLS_CT_ALERT:
  	case TLS_CT_HANDSHAKE:
  		break;
  
  	default:
  		TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
  				"Unknown content type %i", type);
  		return false;
  	}
  
  	if (tls->message_buf_len && type != tls->message_content_type) {
  		TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
  				"Message fragment type %i doesn't match "
  				"previous type %i", type,
  				tls->message_content_type);
  		return false;
  	}
  
  	tls->message_content_type = type;
  
  	while (1) {
  		int header_len, need_len;
  		int chunk_len;
  
  		/* Do we have a full header in tls->message_buf? */
  		header_len = (type == TLS_CT_ALERT) ? 2 : 4;
  		need_len = header_len;
  
  		if (tls->message_buf_len >= header_len) {
  			if (type == TLS_CT_HANDSHAKE) {
  				uint32_t hs_len = (tls->message_buf[1] << 16) |
  					(tls->message_buf[2] << 8) |
  					(tls->message_buf[3] << 0);
  				if (hs_len > (1 << 14)) {
  					TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR,
  							0, "Handshake message "
  							"too long: %i",
  							(int) hs_len);
  					return false;
  				}
  
  				need_len += hs_len;
  			}
  
  			/* Do we have a full structure? */
  			if (tls->message_buf_len == need_len) {
  				if (!tls_handle_message(tls, tls->message_buf,
  							need_len, type,
  							version))
  					return false;
  
  				tls->message_buf_len = 0;
  
  				if (tls->record_flush)
  					break;
  
  				continue;
  			}
  
  			if (!len)
  				break;
  		}
  
  		/* Try to fill up tls->message_buf up to need_len */
  		if (tls->message_buf_max_len < need_len) {
  			tls->message_buf_max_len = need_len;
  			tls->message_buf =
  				l_realloc(tls->message_buf, need_len);
  		}
  
  		need_len -= tls->message_buf_len;
  		chunk_len = need_len;
  		if (len < chunk_len)
  			chunk_len = len;
  
  		memcpy(tls->message_buf + tls->message_buf_len, plaintext,
  				chunk_len);
  		tls->message_buf_len += chunk_len;
  		plaintext += chunk_len;
  		len -= chunk_len;
  
  		if (chunk_len < need_len)
  			break;
  	}
  
  	return true;
  }
  
  static bool tls_handle_ciphertext(struct l_tls *tls)
  {
  	uint8_t type;
  	uint16_t version;
  	uint16_t fragment_len;
  	uint8_t mac_buf[TX_RECORD_MAX_MAC], i, padding_len;
  	int cipher_output_len, error;
  	uint8_t *compressed;
  	int compressed_len;
  	uint8_t iv[32];
  	uint8_t *assocdata;
  
  	type = tls->record_buf[0];
  	version = l_get_be16(tls->record_buf + 1);
  	fragment_len = l_get_be16(tls->record_buf + 3);
  
  	if (fragment_len > (1 << 14) + 2048) {
  		TLS_DISCONNECT(TLS_ALERT_RECORD_OVERFLOW, 0,
  				"Record fragment too long: %u", fragment_len);
  		return false;
  	}
  
  	if ((tls->negotiated_version && tls->negotiated_version != version) ||
  			(!tls->negotiated_version &&
  			 tls->record_buf[1] != 0x03 /* Appending E.1 */)) {
  		TLS_DISCONNECT(TLS_ALERT_PROTOCOL_VERSION, 0,
  				"Record version mismatch: %02x", version);
  		return false;
  	}
  
  	if (fragment_len < tls->mac_length[0]) {
  		TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
  				"Record fragment too short: %u", fragment_len);
  		return false;
  	}
  
  	compressed = alloca(8 + 5 + fragment_len);
  	/* Copy the type and version fields */
  	compressed[8] = type;
  	l_put_be16(version, compressed + 9);
  
  	switch (tls->cipher_type[0]) {
  	case TLS_CIPHER_STREAM:
  		cipher_output_len = fragment_len;
  		compressed_len = cipher_output_len - tls->mac_length[0];
  		l_put_be16(compressed_len, compressed + 11);
  
  		if (!tls->cipher[0])
  			memcpy(compressed + 13, tls->record_buf + 5,
  					cipher_output_len);
  		else if (!l_cipher_decrypt(tls->cipher[0], tls->record_buf + 5,
  						compressed + 13,
  						cipher_output_len)) {
  			TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
  					"Decrypting record fragment failed");
  			return false;
  		}
  
  		/* Calculate the MAC if needed */
  		tls_write_mac(tls, compressed + 8, 5 + compressed_len,
  				mac_buf, false);
  
  		if (memcmp(mac_buf, compressed + 13 + compressed_len,
  							tls->mac_length[0])) {
  			TLS_DISCONNECT(TLS_ALERT_BAD_RECORD_MAC, 0,
  					"Record fragment MAC mismatch");
  			return false;
  		}
  
  		compressed += 13;
  
  		break;
  
  	case TLS_CIPHER_BLOCK:
  		i = 0;
  		if (tls->negotiated_version >= L_TLS_V11)
  			i = tls->record_iv_length[0];
  
  		if (fragment_len <= tls->mac_length[0] + i) {
  			TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
  					"Record fragment too short: %u",
  					fragment_len);
  			return false;
  		}
  
  		cipher_output_len = fragment_len - i;
  
  		if (cipher_output_len % tls->block_length[0] != 0) {
  			/*
  			 * In strict TLS 1.0 TLS_ALERT_DECRYPT_FAIL_RESERVED
  			 * should be returned here but that was declared
  			 * unsafe in the TLS 1.1 spec.
  			 */
  			TLS_DISCONNECT(TLS_ALERT_BAD_RECORD_MAC, 0,
  					"Fragment data len %i not a multiple "
  					"of block length %zi",
  					cipher_output_len,
  					tls->block_length[0]);
  			return false;
  		}
  
  		if (tls->negotiated_version >= L_TLS_V12) {
  			if (!l_cipher_set_iv(tls->cipher[0],
  						tls->record_buf + 5,
  						tls->record_iv_length[0])) {
  				TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
  						"Setting fragment IV failed");
  				return false;
  			}
  		} else if (tls->negotiated_version >= L_TLS_V11)
  			if (!l_cipher_decrypt(tls->cipher[0],
  						tls->record_buf + 5, iv,
  						tls->record_iv_length[0])) {
  				TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
  						"Setting fragment IV failed");
  				return false;
  			}
  
  		if (!l_cipher_decrypt(tls->cipher[0], tls->record_buf + 5 + i,
  					compressed + 13, cipher_output_len)) {
  			TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
  					"Fragment decryption failed");
  			return false;
  		}
  
  		/*
  		 * RFC 5246, page 24:
  		 * In order to defend against this attack, implementations
  		 * MUST ensure that record processing time is essentially the
  		 * same whether or not the padding is correct.  In general,
  		 * the best way to do this is to compute the MAC even if the
  		 * padding is incorrect, and only then reject the packet.  For
  		 * instance, if the pad appears to be incorrect, the
  		 * implementation might assume a zero-length pad and then
  		 * compute the MAC.
  		 */
  		padding_len = compressed[13 + cipher_output_len - 1];
  		error = 0;
  		if (padding_len + tls->mac_length[0] + 1 >
  				(size_t) cipher_output_len) {
  			/*
  			 * In strict TLS 1.0 TLS_ALERT_DECRYPT_FAIL_RESERVED
  			 * should be returned here but that was declared
  			 * unsafe in the TLS 1.1 spec.
  			 */
  			padding_len = 0;
  			error = 1;
  		}
  
  		compressed_len = cipher_output_len - 1 - padding_len -
  			tls->mac_length[0];
  		l_put_be16(compressed_len, compressed + 11);
  
  		error |= !l_secure_memeq(compressed + 13 + cipher_output_len -
  						1 - padding_len, padding_len,
  						padding_len);
  
  		/* Calculate the MAC if needed */
  		tls_write_mac(tls, compressed + 8, 5 + compressed_len,
  				mac_buf, false);
  
  		if ((tls->mac_length[0] && memcmp(mac_buf, compressed + 13 +
  					compressed_len, tls->mac_length[0])) ||
  				error) {
  			TLS_DISCONNECT(TLS_ALERT_BAD_RECORD_MAC, 0,
  					"Record fragment MAC mismatch");
  			return false;
  		}
  
  		compressed += 13;
  
  		break;
  
  	case TLS_CIPHER_AEAD:
  		if (fragment_len <= tls->record_iv_length[0] +
  				tls->auth_tag_length[0]) {
  			TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
  					"Record fragment too short: %u",
  					fragment_len);
  			return false;
  		}
  
  		compressed_len = fragment_len - tls->record_iv_length[0] -
  			tls->auth_tag_length[0];
  		l_put_be16(compressed_len, compressed + 11);
  
  		/* Prepend seq_num to TLSCompressed.type + .version + .length */
  		assocdata = compressed;
  		l_put_be64(tls->seq_num[0]++, assocdata);
  		compressed += 13;
  
  		/* Build the IV */
  		memcpy(iv, tls->fixed_iv[0], tls->fixed_iv_length[0]);
  		memcpy(iv + tls->fixed_iv_length[0], tls->record_buf + 5,
  			tls->record_iv_length[0]);
  
  		if (!l_aead_cipher_decrypt(tls->aead_cipher[0],
  				tls->record_buf + 5 + tls->record_iv_length[0],
  				fragment_len - tls->record_iv_length[0],
  				assocdata, 13, iv, tls->fixed_iv_length[0] +
  				tls->record_iv_length[0],
  				compressed, compressed_len)) {
  			TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
  					"Decrypting record fragment failed");
  			return false;
  		}
  
  		break;
  
  	default:
  		return false;
  	}
  
  	/* DEFLATE not supported so just pass on compressed / compressed_len */
  
  	return tls_handle_plaintext(tls, compressed, compressed_len,
  					type, version);
  }
  
  LIB_EXPORT void l_tls_handle_rx(struct l_tls *tls, const uint8_t *data,
  				size_t len)
  {
  	int need_len;
  	int chunk_len;
  
  	tls->record_flush = false;
  
  	/* Reassemble TLSCiphertext structures from the received chunks */
  
  	while (1) {
  		/* Do we have a full header in tls->record_buf? */
  		if (tls->record_buf_len >= 5) {
  			need_len = 5 + l_get_be16(tls->record_buf + 3);
  
  			/* Do we have a full structure? */
  			if (tls->record_buf_len == need_len) {
  				if (!tls_handle_ciphertext(tls))
  					return;
  
  				tls->record_buf_len = 0;
  				need_len = 5;
  
  				if (tls->record_flush)
  					break;
  			}
  
  			if (!len)
  				break;
  		} else
  			need_len = 5;
  
  		/* Try to fill up tls->record_buf up to need_len */
  		if (tls->record_buf_max_len < need_len) {
  			tls->record_buf_max_len = need_len;
  			tls->record_buf = l_realloc(tls->record_buf, need_len);
  		}
  
  		need_len -= tls->record_buf_len;
  		chunk_len = need_len;
  		if (len < (size_t) chunk_len)
  			chunk_len = len;
  
  		memcpy(tls->record_buf + tls->record_buf_len, data, chunk_len);
  		tls->record_buf_len += chunk_len;
  		data += chunk_len;
  		len -= chunk_len;
  
  		if (chunk_len < need_len)
  			break;
  	}
  }
  