From 9c632c5c674609e79dc45c119ccc0975a99cf5b9 Mon Sep 17 00:00:00 2001
From: Stefan Becker <chemobejk@gmail.com>
Date: Sat, 29 Aug 2015 17:29:40 +0300
Subject: [PATCH] Fix #285 - backport for 1.18.x/1.19.x

This patch helps package maintainers to add TLS-DSK AES-128/256-CBC
support to pidgin-sipe 1.18.x/1.19.x packages. This new feature is
now mandatory for Office 365 users.
---
 src/core/sipe-crypt-nss.c     |  31 ++++--
 src/core/sipe-crypt-openssl.c |  59 ++++++++++--
 src/core/sipe-crypt.h         |   8 +-
 src/core/sipe-tls-tester.c    |  12 ++-
 src/core/sipe-tls.c           | 214 +++++++++++++++++++++++++++++++++---------
 5 files changed, 263 insertions(+), 61 deletions(-)

diff --git a/src/core/sipe-crypt-nss.c b/src/core/sipe-crypt-nss.c
index aef50f0..1c0a13c 100755
--- a/src/core/sipe-crypt-nss.c
+++ b/src/core/sipe-crypt-nss.c
@@ -3,7 +3,7 @@
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  * Copyright (C) 2010 pier11 <pier11@operamail.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -75,7 +75,9 @@ void sipe_crypto_shutdown(void)
 /* PRIVATE methods */
 
 static PK11Context*
-sipe_crypt_ctx_create(CK_MECHANISM_TYPE cipherMech, const guchar *key, gsize key_length)
+sipe_crypt_ctx_create(CK_MECHANISM_TYPE cipherMech,
+		      const guchar *key, gsize key_length,
+		      const guchar *iv, gsize iv_length)
 {
 	PK11SlotInfo* slot;
 	SECItem keyItem;
@@ -95,8 +97,8 @@ sipe_crypt_ctx_create(CK_MECHANISM_TYPE cipherMech, const guchar *key, gsize key
 
 	/* Parameter for crypto context */
 	ivItem.type = siBuffer;
-	ivItem.data = NULL;
-	ivItem.len = 0;
+	ivItem.data = (unsigned char *)iv;
+	ivItem.len = iv_length;
 	SecParam = PK11_ParamFromIV(cipherMech, &ivItem);
 
 	EncContext = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, SymKey, SecParam);
@@ -130,7 +132,7 @@ sipe_crypt(CK_MECHANISM_TYPE cipherMech,
 {
 	void *EncContext;
 
-	EncContext = sipe_crypt_ctx_create(cipherMech, key, key_length);
+	EncContext = sipe_crypt_ctx_create(cipherMech, key, key_length, NULL, 0);
 	sipe_crypt_ctx_encrypt(EncContext, plaintext, plaintext_length, encrypted_text);
 	sipe_crypt_ctx_destroy(EncContext);
 }
@@ -229,7 +231,7 @@ gboolean sipe_crypt_verify_rsa(gpointer public,
 gpointer
 sipe_crypt_ft_start(const guchar *key)
 {
-	return sipe_crypt_ctx_create(CKM_RC4, key, 16);
+	return sipe_crypt_ctx_create(CKM_RC4, key, 16, NULL, 0);
 }
 
 void
@@ -253,7 +255,7 @@ sipe_crypt_ft_destroy(gpointer context)
  */
 gpointer sipe_crypt_tls_start(const guchar *key, gsize key_length)
 {
-	return sipe_crypt_ctx_create(CKM_RC4, key, key_length);
+	return sipe_crypt_ctx_create(CKM_RC4, key, key_length, NULL, 0);
 }
 
 void sipe_crypt_tls_stream(gpointer context,
@@ -268,6 +270,21 @@ void sipe_crypt_tls_destroy(gpointer context)
 	sipe_crypt_ctx_destroy(context);
 }
 
+/* Block AES-CBC cipher for TLS */
+void sipe_crypt_tls_block(const guchar *key, gsize key_length,
+			  const guchar *iv, gsize iv_length,
+			  const guchar *in, gsize length,
+			  guchar *out)
+{
+	PK11Context* context = sipe_crypt_ctx_create(CKM_AES_CBC,
+						     key, key_length,
+						     iv, iv_length);
+	if (context) {
+		sipe_crypt_ctx_encrypt(context, in, length, out);
+		sipe_crypt_ctx_destroy(context);
+	}
+}
+
 /*
   Local Variables:
   mode: c
diff --git a/src/core/sipe-crypt-openssl.c b/src/core/sipe-crypt-openssl.c
index 969fa87..90b99ed 100755
--- a/src/core/sipe-crypt-openssl.c
+++ b/src/core/sipe-crypt-openssl.c
@@ -3,7 +3,7 @@
  *
  * pidgin-sipe
  *
- * Copyright (C) 2013 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2013-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -149,17 +149,20 @@ gboolean sipe_crypt_verify_rsa(gpointer public,
 			  public));
 }
 
-static gpointer openssl_rc4_init(const guchar *key, gsize key_length)
+static gpointer openssl_EVP_init(const EVP_CIPHER *type,
+				 const guchar *key,
+				 gsize key_length,
+				 const guchar *iv)
 {
 	EVP_CIPHER_CTX *ctx = g_malloc(sizeof(EVP_CIPHER_CTX));
 
 	/* initialize context */
 	EVP_CIPHER_CTX_init(ctx);
-	EVP_EncryptInit_ex(ctx, EVP_rc4(), NULL, key, NULL);
+	EVP_EncryptInit_ex(ctx, type, NULL, key, iv);
 
 	/* set encryption parameters */
 	EVP_CIPHER_CTX_set_key_length(ctx, key_length);
-	EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL);
+	EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv);
 
 	return(ctx);
 }
@@ -167,7 +170,7 @@ static gpointer openssl_rc4_init(const guchar *key, gsize key_length)
 /* Stream RC4 cipher for file transfer with fixed-length 128-bit key */
 gpointer sipe_crypt_ft_start(const guchar *key)
 {
-	return(openssl_rc4_init(key, 16));
+	return(openssl_EVP_init(EVP_rc4(), key, 16, NULL));
 }
 
 void sipe_crypt_ft_stream(gpointer context,
@@ -187,7 +190,7 @@ void sipe_crypt_ft_destroy(gpointer context)
 /* Stream RC4 cipher for TLS with variable key length */
 gpointer sipe_crypt_tls_start(const guchar *key, gsize key_length)
 {
-	return(openssl_rc4_init(key, key_length));
+	return(openssl_EVP_init(EVP_rc4(), key, key_length, NULL));
 }
 
 void sipe_crypt_tls_stream(gpointer context,
@@ -204,6 +207,50 @@ void sipe_crypt_tls_destroy(gpointer context)
 	g_free(context);
 }
 
+/* Block AES-CBC cipher for TLS */
+void sipe_crypt_tls_block(const guchar *key, gsize key_length,
+			  const guchar *iv,
+			  /* OpenSSL assumes that iv is of correct size */
+			  SIPE_UNUSED_PARAMETER gsize iv_length,
+			  const guchar *in, gsize length,
+			  guchar *out)
+{
+	const EVP_CIPHER *type = NULL;
+
+	switch (key_length) {
+	case 128 / 8:
+		type = EVP_aes_128_cbc();
+		break;
+	/*
+	 * TLS does not use AES-192
+	 *
+	case 192 / 8:
+		type = EVP_aes_192_cbc();
+		break;
+	*/
+	case 256 / 8:
+		type = EVP_aes_256_cbc();
+		break;
+	default:
+		SIPE_DEBUG_ERROR("sipe_crypt_tls_block: unsupported key length %" G_GSIZE_FORMAT " bytes for AES CBC",
+				 key_length);
+		break;
+	}
+
+	if (type) {
+		EVP_CIPHER_CTX *context = openssl_EVP_init(type,
+							   key, key_length,
+							   iv);
+
+		if (context) {
+			int tmp;
+			EVP_EncryptUpdate(context, out, &tmp, in, length);
+			EVP_CIPHER_CTX_cleanup(context);
+			g_free(context);
+		}
+	}
+}
+
 /*
   Local Variables:
   mode: c
diff --git a/src/core/sipe-crypt.h b/src/core/sipe-crypt.h
index 596324d..d8f2a9c 100644
--- a/src/core/sipe-crypt.h
+++ b/src/core/sipe-crypt.h
@@ -3,7 +3,7 @@
  *
  * pidgin-sipe
  *
- * Copyright (C) 2010-11 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -65,3 +65,9 @@ void sipe_crypt_tls_stream(gpointer context,
 			   const guchar *in, gsize length,
 			   guchar *out);
 void sipe_crypt_tls_destroy(gpointer context);
+
+/* Block AES-CBC cipher for TLS */
+void sipe_crypt_tls_block(const guchar *key, gsize key_length,
+			  const guchar *iv, gsize iv_length,
+			  const guchar *in, gsize length,
+			  guchar *out);
diff --git a/src/core/sipe-tls-tester.c b/src/core/sipe-tls-tester.c
index 31db061..d0e3d73 100644
--- a/src/core/sipe-tls-tester.c
+++ b/src/core/sipe-tls-tester.c
@@ -3,7 +3,7 @@
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-12 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
@@ -34,7 +34,9 @@
  *
  *    $ openssl s_server -accept 8443 -debug -msg \
  *              -cert server.cert -key server.pem \
- *              -tls1 -verify 0 -cipher RC4-SHA
+ *              -tls1 -verify 0 [ -cipher <c1>[:<c2>...] ]
+ *
+ *   ciphers: RC4-MD5, RC4-SHA, AES128-SHA, AES256-SHA
  *
  * - Running the test program in another shell:
  *
@@ -178,6 +180,8 @@ static guchar *read_tls_record(int fd,
 static void tls_handshake(struct sipe_tls_state *state,
 			  int fd)
 {
+	gboolean success = FALSE;
+
 	printf("TLS handshake starting...\n");
 
 	/* generate next handshake message */
@@ -186,7 +190,7 @@ static void tls_handshake(struct sipe_tls_state *state,
 
 		/* handshake completed? */
 		if (!state->out_buffer) {
-			printf("Handshake completed.\n");
+			success = TRUE;
 			break;
 		}
 
@@ -213,7 +217,7 @@ static void tls_handshake(struct sipe_tls_state *state,
 		}
 	}
 
-	printf("TLS handshake done.\n");
+	printf("TLS handshake %s.\n", success ? "SUCCESSFUL" : "FAILED");
 }
 
 
diff --git a/src/core/sipe-tls.c b/src/core/sipe-tls.c
index 641d221..c5a2835 100644
--- a/src/core/sipe-tls.c
+++ b/src/core/sipe-tls.c
@@ -3,7 +3,7 @@
  *
  * pidgin-sipe
  *
- * Copyright (C) 2011-12 SIPE Project <http://sipe.sourceforge.net/>
+ * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
  *
  *
  * This program is free software; you can redistribute it and/or modify
@@ -21,7 +21,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  *
- * TLS Protocol Version 1.0/1.1 - Handshake Messages
+ * TLS Protocol Version 1.0/1.1/1.2 - Handshake Messages
  *
  * TLS-DSK uses the handshake messages during authentication and session key
  * exchange. This module *ONLY* implements this part of the TLS specification!
@@ -31,6 +31,7 @@
  *   - RFC2246: http://www.ietf.org/rfc/rfc2246.txt
  *   - RFC3546: http://www.ietf.org/rfc/rfc3546.txt
  *   - RFC4346: http://www.ietf.org/rfc/rfc4346.txt
+ *   - RFC5246: http://www.ietf.org/rfc/rfc5246.txt
  */
 
 #include <stdlib.h>
@@ -81,12 +82,16 @@ struct tls_internal_state {
 	const guchar *server_write_mac_secret;
 	const guchar *client_write_secret;
 	const guchar *server_write_secret;
+	const guchar *client_write_iv;
+	const guchar *server_write_iv;
 	void (*mac_func)(const guchar *key, gsize key_length,
 			 const guchar *data, gsize data_length,
 			 guchar *digest);
 	gpointer cipher_context;
 	guint64 sequence_number;
+	gboolean stream_cipher;
 	gboolean encrypted;
+	gboolean expected;
 };
 
 /*
@@ -100,11 +105,16 @@ struct tls_internal_state {
 
 #define TLS_PROTOCOL_VERSION_1_0 0x0301
 #define TLS_PROTOCOL_VERSION_1_1 0x0302
+#define TLS_PROTOCOL_VERSION_1_2 0x0303
 
 /* CipherSuites */
 #define TLS_RSA_EXPORT_WITH_RC4_40_MD5 0x0003
 #define TLS_RSA_WITH_RC4_128_MD5       0x0004
 #define TLS_RSA_WITH_RC4_128_SHA       0x0005
+#define TLS_RSA_WITH_AES_128_CBC_SHA   0x002F
+#define TLS_RSA_WITH_AES_256_CBC_SHA   0x0035
+
+#define TLS_AES_CBC_BLOCK_LENGTH 16 /* bytes */
 
 /* CompressionMethods */
 #define TLS_COMP_METHOD_NULL 0
@@ -203,7 +213,7 @@ struct tls_compile_sessionid {
 
 struct tls_compile_cipher {
 	gsize elements; /* VECTOR */
-	guint suites[3];
+	guint suites[5];
 };
 
 struct tls_compile_compression {
@@ -276,6 +286,9 @@ static void debug_hex(struct tls_internal_state *state,
 #define debug_printf(state, format, ...) \
 	if (state->debug) g_string_append_printf(state->debug, format, __VA_ARGS__)
 
+/* Analyzer only needs the debugging functions */
+#ifndef _SIPE_COMPILING_ANALYZER
+
 static void debug_secrets(struct tls_internal_state *state,
 			  const gchar *label,
 			  const guchar *secret,
@@ -471,6 +484,8 @@ static guchar *sipe_tls_prf(SIPE_UNUSED_PARAMETER struct tls_internal_state *sta
 	return(md5);
 }
 
+#endif /* !_SIPE_COMPILING_ANALYZER */
+
 /*
  * TLS data parsers
  *
@@ -539,6 +554,10 @@ static gboolean parse_array(struct tls_internal_state *state,
 		return(FALSE);
 	debug_printf(state, "%s/ARRAY[%" G_GSIZE_FORMAT "]\n",
 		     desc->label, desc->max);
+#ifdef _SIPE_COMPILING_ANALYZER
+	if (desc->max)
+		debug_hex(state, desc->max);
+#endif
 	if (state->data) {
 		struct tls_parsed_array *save = g_malloc0(sizeof(struct tls_parsed_array) +
 							  desc->max);
@@ -567,6 +586,10 @@ static gboolean parse_vector(struct tls_internal_state *state,
 		return(FALSE);
 	}
 	debug_printf(state, "%s/VECTOR<%d>\n", desc->label, length);
+#ifdef _SIPE_COMPILING_ANALYZER
+	if (length)
+		debug_hex(state, length);
+#endif
 	if (state->data) {
 		struct tls_parsed_array *save = g_malloc0(sizeof(struct tls_parsed_array) +
 							  length);
@@ -773,7 +796,8 @@ static const struct msg_descriptor Finished_m = {
 /*
  * TLS message parsers
  */
-static gboolean handshake_parse(struct tls_internal_state *state)
+static gboolean handshake_parse(struct tls_internal_state *state,
+				guint expected_type)
 {
 	const guchar *bytes = state->msg_current;
 	gsize length        = state->msg_remainder;
@@ -809,6 +833,9 @@ static gboolean handshake_parse(struct tls_internal_state *state)
 		debug_printf(state, "TLS handshake (%" G_GSIZE_FORMAT " bytes) (%d)",
 			     msg_length, msg_type);
 
+		if (msg_type == expected_type)
+			state->expected = TRUE;
+
 		state->msg_current   = (guchar *) bytes + TLS_HANDSHAKE_HEADER_LENGTH;
 		state->msg_remainder = msg_length;
 
@@ -816,6 +843,7 @@ static gboolean handshake_parse(struct tls_internal_state *state)
 			const struct layout_descriptor *ldesc = desc->layouts;
 
 			debug_printf(state, "%s\n", desc->description);
+
 			while (TLS_LAYOUT_IS_VALID(ldesc)) {
 				success = ldesc->parser(state, ldesc);
 				if (!success)
@@ -851,7 +879,8 @@ static void free_parse_data(struct tls_internal_state *state)
 }
 
 static gboolean tls_record_parse(struct tls_internal_state *state,
-				 gboolean incoming)
+				 gboolean incoming,
+				 guint expected)
 {
 	const guchar *bytes  = incoming ? state->common.in_buffer : state->common.out_buffer;
 	gsize length         = incoming ? state->common.in_length : state->common.out_length;
@@ -860,13 +889,22 @@ static gboolean tls_record_parse(struct tls_internal_state *state,
 	gsize record_length;
 	gboolean success = TRUE;
 
+	/* reject empty incoming messages */
+	if (incoming && (length == 0)) {
+		SIPE_DEBUG_ERROR_NOFORMAT("tls_record_parse: empty TLS message received");
+		return(FALSE);
+	}
+
+#ifndef _SIPE_COMPILING_ANALYZER
 	debug_printf(state, "TLS MESSAGE %s\n", incoming ? "INCOMING" : "OUTGOING");
+#endif
 
 	/* Collect parser data for incoming messages */
 	if (incoming)
 		state->data = g_hash_table_new_full(g_str_hash, g_str_equal,
 						    NULL, g_free);
 
+	state->expected = FALSE;
 	while (success && (length > 0)) {
 
 		/* truncated header check */
@@ -891,6 +929,9 @@ static gboolean tls_record_parse(struct tls_internal_state *state,
 		case TLS_PROTOCOL_VERSION_1_1:
 			version_str = "1.1 (RFC4346)";
 			break;
+		case TLS_PROTOCOL_VERSION_1_2:
+			version_str = "1.2 (RFC5246)";
+			break;
 		default:
 			version_str = "<future protocol version>";
 			break;
@@ -911,6 +952,8 @@ static gboolean tls_record_parse(struct tls_internal_state *state,
 		state->msg_current   = (guchar *) bytes + TLS_RECORD_HEADER_LENGTH;
 		state->msg_remainder = record_length - TLS_RECORD_HEADER_LENGTH;
 
+/* Analyzer only needs the debugging functions */
+#ifndef _SIPE_COMPILING_ANALYZER
 		/* Add incoming message contents to digest contexts */
 		if (incoming) {
 			sipe_digest_md5_update(state->md5_context,
@@ -920,11 +963,15 @@ static gboolean tls_record_parse(struct tls_internal_state *state,
 						state->msg_current,
 						state->msg_remainder);
 		}
+#endif /* !_SIPE_COMPILING_ANALYZER */
 
 		switch (bytes[TLS_RECORD_OFFSET_TYPE]) {
 		case TLS_RECORD_TYPE_CHANGE_CIPHER_SPEC:
 			debug_print(state, "Change Cipher Spec\n");
-			if (incoming) state->encrypted = TRUE;
+			if (incoming)
+				state->encrypted = TRUE;
+			if (expected == TLS_RECORD_TYPE_CHANGE_CIPHER_SPEC)
+				state->expected = TRUE;
 			break;
 
 		case TLS_RECORD_TYPE_HANDSHAKE:
@@ -932,7 +979,7 @@ static gboolean tls_record_parse(struct tls_internal_state *state,
 				debug_print(state, "Encrypted handshake message\n");
 				debug_hex(state, 0);
 			} else {
-				success = handshake_parse(state);
+				success = handshake_parse(state, expected);
 			}
 			break;
 
@@ -947,6 +994,14 @@ static gboolean tls_record_parse(struct tls_internal_state *state,
 		length -= record_length;
 	}
 
+#ifndef _SIPE_COMPILING_ANALYZER
+	if (incoming && !state->expected) {
+		SIPE_DEBUG_ERROR("tls_record_parse: did not find expected msg type %d",
+				 expected);
+		success = FALSE;
+	}
+#endif
+
 	if (!success)
 		free_parse_data(state);
 
@@ -958,6 +1013,9 @@ static gboolean tls_record_parse(struct tls_internal_state *state,
 	return(success);
 }
 
+/* Analyzer only needs the debugging functions */
+#ifndef _SIPE_COMPILING_ANALYZER
+
 /*
  * TLS message compiler
  */
@@ -1007,12 +1065,14 @@ static void compile_encrypted_tls_record(struct tls_internal_state *state,
 					 const struct tls_compiled_message *msg)
 {
 	guchar *plaintext;
-	gsize plaintext_length;
+	gsize plaintext_length; /* header + content        */
 	guchar *mac;
 	gsize mac_length;
 	guchar *message;
 	guchar *encrypted;
-	gsize encrypted_length;
+	gsize message_length;   /* header + content + MAC  */
+	gsize padding_length;   /* for block cipher        */
+	gsize encrypted_length; /* header + encrypted data */
 
 	/* Create plaintext TLS record */
 	compile_tls_record(state, msg, NULL);
@@ -1022,16 +1082,23 @@ static void compile_encrypted_tls_record(struct tls_internal_state *state,
 		return;
 
 	/* Prepare encryption buffer */
-	encrypted_length = plaintext_length + state->mac_length;
+	message_length = plaintext_length + state->mac_length;
+	if (state->stream_cipher) {
+		padding_length   = 0;
+		encrypted_length = message_length;
+	} else {
+		padding_length   = TLS_AES_CBC_BLOCK_LENGTH - (message_length - TLS_RECORD_HEADER_LENGTH + 1) % TLS_AES_CBC_BLOCK_LENGTH;
+		encrypted_length = message_length + padding_length + 1;
+	}
 	SIPE_DEBUG_INFO("compile_encrypted_tls_record: total size %" G_GSIZE_FORMAT,
 			encrypted_length - TLS_RECORD_HEADER_LENGTH);
-	message          = g_malloc(encrypted_length);
+	message = g_malloc(message_length);
 	memcpy(message, plaintext, plaintext_length);
 	lowlevel_integer_to_tls(message + TLS_RECORD_OFFSET_LENGTH, 2,
 				encrypted_length - TLS_RECORD_HEADER_LENGTH);
 
 	/*
-	 * Calculate MAC
+	 * Calculate MAC and append to message
 	 *
 	 * HMAC_hash(client_write_mac_secret,
 	 *           sequence_number + type + version + length + fragment)
@@ -1051,13 +1118,36 @@ static void compile_encrypted_tls_record(struct tls_internal_state *state,
 			message + plaintext_length);
 	g_free(mac);
 
-	/* Encrypt message + MAC */
 	encrypted = g_malloc(encrypted_length);
+	/* header (unencrypted) */
 	memcpy(encrypted, message, TLS_RECORD_HEADER_LENGTH);
-	sipe_crypt_tls_stream(state->cipher_context,
-			      message + TLS_RECORD_HEADER_LENGTH,
-			      encrypted_length - TLS_RECORD_HEADER_LENGTH,
-			      encrypted + TLS_RECORD_HEADER_LENGTH);
+	if (state->stream_cipher) {
+		/* ENCRYPT(content + MAC) */
+		sipe_crypt_tls_stream(state->cipher_context,
+				      message + TLS_RECORD_HEADER_LENGTH,
+				      encrypted_length - TLS_RECORD_HEADER_LENGTH,
+				      encrypted + TLS_RECORD_HEADER_LENGTH);
+	} else {
+		/* TLS 1.0 GenericBlockCipher */
+		/* content + MAC */
+		memcpy(encrypted + TLS_RECORD_HEADER_LENGTH,
+		       message + TLS_RECORD_HEADER_LENGTH,
+		       message_length - TLS_RECORD_HEADER_LENGTH);
+
+		/* padding + padding_length */
+		memset(encrypted + message_length,
+		       padding_length,
+		       padding_length + 1);
+
+		/* ENCRYPT(content + MAC + padding + padding_length) */
+		sipe_crypt_tls_block(state->client_write_secret,
+				     state->key_length,
+				     state->client_write_iv,
+				     TLS_AES_CBC_BLOCK_LENGTH,
+				     encrypted + TLS_RECORD_HEADER_LENGTH,
+				     encrypted_length - TLS_RECORD_HEADER_LENGTH,
+				     encrypted + TLS_RECORD_HEADER_LENGTH);
+	}
 	g_free(message);
 
 	/* swap buffers */
@@ -1154,7 +1244,8 @@ static gboolean check_cipher_suite(struct tls_internal_state *state)
 {
 	struct tls_parsed_integer *cipher_suite = g_hash_table_lookup(state->data,
 								      "CipherSuite");
-	const gchar *label = NULL;
+	const gchar *label_mac    = NULL;
+	const gchar *label_cipher = NULL;
 
 	if (!cipher_suite) {
 		SIPE_DEBUG_ERROR_NOFORMAT("check_cipher_suite: server didn't specify the cipher suite");
@@ -1163,26 +1254,52 @@ static gboolean check_cipher_suite(struct tls_internal_state *state)
 
 	switch (cipher_suite->value) {
 	case TLS_RSA_EXPORT_WITH_RC4_40_MD5:
-		state->mac_length = SIPE_DIGEST_HMAC_MD5_LENGTH;
-		state->key_length = 40 / 8;
-		state->mac_func   = sipe_digest_hmac_md5;
-		label             = "MD5";
+		state->mac_length       = SIPE_DIGEST_HMAC_MD5_LENGTH;
+		state->key_length       = 40 / 8;
+		state->mac_func         = sipe_digest_hmac_md5;
+		state->stream_cipher    = TRUE;
+		label_mac               = "MD5";
+		label_cipher            = "RC4 stream";
 		state->common.algorithm = SIPE_TLS_DIGEST_ALGORITHM_MD5;
 		break;
 
 	case TLS_RSA_WITH_RC4_128_MD5:
-		state->mac_length = SIPE_DIGEST_HMAC_MD5_LENGTH;
-		state->key_length = 128 / 8;
-		state->mac_func   = sipe_digest_hmac_md5;
-		label             = "MD5";
+		state->mac_length       = SIPE_DIGEST_HMAC_MD5_LENGTH;
+		state->key_length       = 128 / 8;
+		state->mac_func         = sipe_digest_hmac_md5;
+		state->stream_cipher    = TRUE;
+		label_mac               = "MD5";
+		label_cipher            = "RC4 stream";
 		state->common.algorithm = SIPE_TLS_DIGEST_ALGORITHM_MD5;
 		break;
 
 	case TLS_RSA_WITH_RC4_128_SHA:
-		state->mac_length = SIPE_DIGEST_HMAC_SHA1_LENGTH;
-		state->key_length = 128 / 8;
-		state->mac_func   = sipe_digest_hmac_sha1;
-		label             = "SHA-1";
+		state->mac_length       = SIPE_DIGEST_HMAC_SHA1_LENGTH;
+		state->key_length       = 128 / 8;
+		state->mac_func         = sipe_digest_hmac_sha1;
+		state->stream_cipher    = TRUE;
+		label_mac               = "SHA-1";
+		label_cipher            = "RC4 stream";
+		state->common.algorithm = SIPE_TLS_DIGEST_ALGORITHM_SHA1;
+		break;
+
+	case TLS_RSA_WITH_AES_128_CBC_SHA:
+		state->mac_length       = SIPE_DIGEST_HMAC_SHA1_LENGTH;
+		state->key_length       = 128 / 8;
+		state->mac_func         = sipe_digest_hmac_sha1;
+		state->stream_cipher    = FALSE;
+		label_mac               = "SHA-1";
+		label_cipher            = "AES-CBC block";
+		state->common.algorithm = SIPE_TLS_DIGEST_ALGORITHM_SHA1;
+		break;
+
+	case TLS_RSA_WITH_AES_256_CBC_SHA:
+		state->mac_length       = SIPE_DIGEST_HMAC_SHA1_LENGTH;
+		state->key_length       = 256 / 8;
+		state->mac_func         = sipe_digest_hmac_sha1;
+		state->stream_cipher    = FALSE;
+		label_mac               = "SHA-1";
+		label_cipher            = "AES-CBC block";
 		state->common.algorithm = SIPE_TLS_DIGEST_ALGORITHM_SHA1;
 		break;
 
@@ -1192,16 +1309,18 @@ static gboolean check_cipher_suite(struct tls_internal_state *state)
 		break;
 	}
 
-	if (label)
-		SIPE_DEBUG_INFO("check_cipher_suite: KEY(stream cipher RC4) %" G_GSIZE_FORMAT ", MAC(%s) %" G_GSIZE_FORMAT,
-				state->key_length, label, state->mac_length);
+	if (label_cipher && label_mac)
+		SIPE_DEBUG_INFO("check_cipher_suite: KEY(%s cipher) %" G_GSIZE_FORMAT ", MAC(%s) %" G_GSIZE_FORMAT,
+				label_cipher, state->key_length,
+				label_mac, state->mac_length);
 
-	return(label != NULL);
+	return(label_cipher && label_mac);
 }
 
 static void tls_calculate_secrets(struct tls_internal_state *state)
 {
-	gsize length = 2 * (state->mac_length + state->key_length);
+	gsize length = 2 * (state->mac_length + state->key_length +
+			    (state->stream_cipher ? 0 : TLS_AES_CBC_BLOCK_LENGTH));
 	guchar *random;
 
 	/* Generate pre-master secret */
@@ -1272,9 +1391,14 @@ static void tls_calculate_secrets(struct tls_internal_state *state)
 	state->client_write_secret     = state->key_block + 2 * state->mac_length;
 	state->server_write_secret     = state->key_block + 2 * state->mac_length + state->key_length;
 
-	/* initialize cipher context */
-	state->cipher_context = sipe_crypt_tls_start(state->client_write_secret,
-						     state->key_length);
+	/* initialize stream cipher context */
+	if (state->stream_cipher) {
+		state->cipher_context = sipe_crypt_tls_start(state->client_write_secret,
+							     state->key_length);
+	} else {
+		state->client_write_iv = state->key_block + 2 * (state->mac_length + state->key_length);
+		state->server_write_iv = state->key_block + 2 * (state->mac_length + state->key_length) + TLS_AES_CBC_BLOCK_LENGTH;
+	}
 }
 
 #if 0 /* NOT NEEDED? */
@@ -1521,10 +1645,12 @@ static gboolean tls_client_hello(struct tls_internal_state *state)
 		{ TLS_PROTOCOL_VERSION_1_0 },
 		{ 0, { 0 } },
 		{ 0 /* empty SessionID */ },
-		{ 3,
+		{ 5,
 		  {
 			  TLS_RSA_WITH_RC4_128_MD5,
 			  TLS_RSA_WITH_RC4_128_SHA,
+			  TLS_RSA_WITH_AES_128_CBC_SHA,
+			  TLS_RSA_WITH_AES_256_CBC_SHA,
 			  TLS_RSA_EXPORT_WITH_RC4_40_MD5
 		  }
 		},
@@ -1551,7 +1677,7 @@ static gboolean tls_client_hello(struct tls_internal_state *state)
 		state->debug = g_string_new("");
 
 	state->state = TLS_HANDSHAKE_STATE_SERVER_HELLO;
-	return(tls_record_parse(state, FALSE));
+	return(tls_record_parse(state, FALSE, 0));
 }
 
 static gboolean tls_server_hello(struct tls_internal_state *state)
@@ -1562,7 +1688,7 @@ static gboolean tls_server_hello(struct tls_internal_state *state)
 	struct tls_compiled_message *finished    = NULL;
 	gboolean success = FALSE;
 
-	if (!tls_record_parse(state, TRUE))
+	if (!tls_record_parse(state, TRUE, TLS_HANDSHAKE_TYPE_SERVER_HELLO))
 		return(FALSE);
 
 	if (((certificate = tls_client_certificate(state))  != NULL) &&
@@ -1573,7 +1699,7 @@ static gboolean tls_server_hello(struct tls_internal_state *state)
 		/* Part 1 */
 		compile_tls_record(state, certificate, exchange, verify, NULL);
 
-		success = tls_record_parse(state, FALSE);
+		success = tls_record_parse(state, FALSE,  0);
 		if (success) {
 			guchar *part1      = state->common.out_buffer;
 			gsize part1_length = state->common.out_length;
@@ -1628,7 +1754,7 @@ static gboolean tls_finished(struct tls_internal_state *state)
 {
 	guchar *random;
 
-	if (!tls_record_parse(state, TRUE))
+	if (!tls_record_parse(state, TRUE, TLS_RECORD_TYPE_CHANGE_CIPHER_SPEC))
 		return(FALSE);
 
 	/* we don't need the data */
@@ -1782,6 +1908,8 @@ void sipe_tls_free(struct sipe_tls_state *state)
 	}
 }
 
+#endif /* !_SIPE_COMPILING_ANALYZER */
+
 /*
   Local Variables:
   mode: c
-- 
2.4.3

