Implement the Opaque PRF Input TLS extension
(draft-rescorla-tls-opaque-prf-input-00.txt), and do some cleanups and
bugfixes on the way.  In particular, this fixes the buffer bounds
checks in ssl_add_clienthello_tlsext() and in ssl_add_serverhello_tlsext().

Note that the opaque PRF Input TLS extension is not compiled by default;
see CHANGES.
diff --git a/CHANGES b/CHANGES
index 106f98f..dddc1ea 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,64 @@
 
  Changes between 0.9.8f and 0.9.9  [xx XXX xxxx]
 
+  *) Implement Opaque PRF Input TLS extension as specified in
+     draft-rescorla-tls-opaque-prf-input-00.txt.  Since this is not an
+     official specification yet and no extension type assignment by
+     IANA exists, this extension (for now) will have to be explicitly
+     enabled when building OpenSSL by providing the extension number
+     to use.  For example, specify an option
+
+         -DTLSEXT_TYPE_opaque_prf_input=0x9527
+
+     to the "config" or "Configure" script to enable the extension,
+     assuming extension number 0x9527 (which is a completely arbitrary
+     and unofficial assignment based on the MD5 hash of the Internet
+     Draft).  Note that by doing so, you potentially lose
+     interoperability with other TLS implementations since these might
+     be using the same extension number for other purposes.
+
+     SSL_set_tlsext_opaque_prf_input(ssl, src, len) is used to set the
+     opaque PRF input value to use in the handshake.  This will create
+     an interal copy of the length-'len' string at 'src', and will
+     return non-zero for success.
+
+     To get more control and flexibility, provide a callback function
+     by using
+
+          SSL_CTX_set_tlsext_opaque_prf_input_callback(ctx, cb)
+          SSL_CTX_set_tlsext_opaque_prf_input_callback_arg(ctx, arg)
+
+     where
+
+          int (*cb)(SSL *, void *peerinput, size_t len, void *arg);
+          void *arg;
+
+     Callback function 'cb' will be called in handshakes, and is
+     expected to use SSL_set_tlsext_opaque_prf_input() as appropriate.
+     Argument 'arg' is for application purposes (the value as given to
+     SSL_CTX_set_tlsext_opaque_prf_input_callback_arg() will directly
+     be provided to the callback function).  The callback function
+     has to return non-zero to report success: usually 1 to use opaque
+     PRF input just if possible, or 2 to enforce use of the opaque PRF
+     input.  In the latter case, the library will abort the handshake
+     if opaque PRF input is not successfully negotiated.
+
+     Arguments 'peerinput' and 'len' given to the callback function
+     will always be NULL and 0 in the case of a client.  A server will
+     see the client's opaque PRF input through these variables if
+     available (NULL and 0 otherwise).  Note that if the server
+     provides an opaque PRF input, the length must be the same as the
+     length of the client's opaque PRF input.
+
+     Note that the callback function will only be called when creating
+     a new session (session resumption can resume whatever was
+     previously negotiated), and will not be called in SSL 2.0
+     handshakes; thus, SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) or
+     SSL_set_options(ssl, SSL_OP_NO_SSLv2) is especially recommended
+     for applications that need to enforce opaque PRF input.
+
+     [Bodo Moeller]
+
   *) Update ssl code to support digests other than SHA1+MD5 for handshake
      MAC. 
 
diff --git a/apps/s_cb.c b/apps/s_cb.c
index 0366cc8..dc50ff5 100644
--- a/apps/s_cb.c
+++ b/apps/s_cb.c
@@ -638,6 +638,11 @@
 		extname = "server ticket";
 		break;
 
+#ifdef TLSEXT_TYPE_opaque_prf_input
+		case TLSEXT_TYPE_opaque_prf_input:
+		extname = "opaque PRF input";
+		break;
+#endif
 
 		default:
 		extname = "unknown";
diff --git a/apps/s_client.c b/apps/s_client.c
index 44c5356..eae1287 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -859,6 +859,11 @@
 		}
 #endif	/* OPENSSL_NO_KRB5  */
 /*	SSL_set_cipher_list(con,"RC4-MD5"); */
+#if 0
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	SSL_set_tlsext_opaque_prf_input(con, "Test client", 1);
+#endif
+#endif
 
 re_start:
 
@@ -1073,12 +1078,14 @@
 			if (in_init)
 				{
 				in_init=0;
+#if 0 /* This test doesn't really work as intended (needs to be fixed) */
 #ifndef OPENSSL_NO_TLSEXT
 				if (servername != NULL && !SSL_session_reused(con))
 					{
 					BIO_printf(bio_c_out,"Server did %sacknowledge servername extension.\n",tlsextcbp.ack?"":"not ");
 					}
 #endif
+#endif
 				if (sess_out)
 					{
 					BIO *stmp = BIO_new_file(sess_out, "w");
diff --git a/apps/s_server.c b/apps/s_server.c
index 328fcaf..0cfdf6a 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -1575,6 +1575,11 @@
 						 strlen((char *)context));
 	}
 	SSL_clear(con);
+#if 0
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	SSL_set_tlsext_opaque_prf_input(con, "Test server", 1);
+#endif
+#endif
 
 	if (SSL_version(con) == DTLS1_VERSION)
 		{
diff --git a/ssl/s23_clnt.c b/ssl/s23_clnt.c
index 1181d05..c500a93 100644
--- a/ssl/s23_clnt.c
+++ b/ssl/s23_clnt.c
@@ -277,6 +277,19 @@
 		version = SSL2_VERSION;
 		}
 
+	if (version != SSL2_VERSION)
+		{
+		/* have to disable SSL 2.0 compatibility if we need TLS extensions */
+
+		if (s->tlsext_hostname != NULL)
+			ssl2_compat = 0;
+		
+#ifdef TLSEXT_TYPE_opaque_prf_input
+		if (s->ctx->tlsext_opaque_prf_input_callback != 0 || s->tlsext_opaque_prf_input != NULL)
+			ssl2_compat = 0;
+#endif
+		}
+
 	buf=(unsigned char *)s->init_buf->data;
 	if (s->state == SSL23_ST_CW_CLNT_HELLO_A)
 		{
@@ -420,6 +433,12 @@
 			*(p++)=0; /* Add the NULL method */
 
 #ifndef OPENSSL_NO_TLSEXT
+			/* TLS extensions*/
+			if (ssl_prepare_clienthello_tlsext(s) <= 0)
+				{
+				SSLerr(SSL_F_SSL23_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT);
+				return -1;
+				}
 			if ((p = ssl_add_clienthello_tlsext(s, p, buf+SSL3_RT_MAX_PLAIN_LENGTH)) == NULL)
 				{
 				SSLerr(SSL_F_SSL23_CLIENT_HELLO,ERR_R_INTERNAL_ERROR);
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 2d1b1a5..f8f43eb 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -656,7 +656,9 @@
 			}
 #endif
 		*(p++)=0; /* Add the NULL method */
+
 #ifndef OPENSSL_NO_TLSEXT
+		/* TLS extensions*/
 		if (ssl_prepare_clienthello_tlsext(s) <= 0)
 			{
 			SSLerr(SSL_F_SSL3_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT);
@@ -853,6 +855,7 @@
 		s->s3->tmp.new_compression=comp;
 		}
 #endif
+
 #ifndef OPENSSL_NO_TLSEXT
 	/* TLS extensions*/
 	if (s->version > SSL3_VERSION)
@@ -1768,7 +1771,7 @@
 	if (ticklen + 6 != n)
 		{
 		al = SSL3_AL_FATAL,SSL_AD_DECODE_ERROR;
-		SSLerr(SSL_F_SSL3_NEW_SESSION_TICKET,SSL_R_LENGTH_MISMATCH);
+		SSLerr(SSL_F_SSL3_GET_NEW_SESSION_TICKET,SSL_R_LENGTH_MISMATCH);
 		goto f_err;
 		}
 	if (s->session->tlsext_tick)
@@ -1779,7 +1782,7 @@
 	s->session->tlsext_tick = OPENSSL_malloc(ticklen);
 	if (!s->session->tlsext_tick)
 		{
-		SSLerr(SSL_F_SSL3_NEW_SESSION_TICKET,ERR_R_MALLOC_FAILURE);
+		SSLerr(SSL_F_SSL3_GET_NEW_SESSION_TICKET,ERR_R_MALLOC_FAILURE);
 		goto err;
 		}
 	memcpy(s->session->tlsext_tick, p, ticklen);
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index b2d1fef..476d27a 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -2083,6 +2083,13 @@
 	if(s == NULL)
 	    return;
 
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	if (s->s3->client_opaque_prf_input != NULL)
+		OPENSSL_free(s->s3->client_opaque_prf_input);
+	if (s->s3->server_opaque_prf_input != NULL)
+		OPENSSL_free(s->s3->server_opaque_prf_input);
+#endif
+
 	ssl3_cleanup_key_block(s);
 	if (s->s3->rbuf.buf != NULL)
 		OPENSSL_free(s->s3->rbuf.buf);
@@ -2115,6 +2122,15 @@
 	unsigned char *rp,*wp;
 	size_t rlen, wlen;
 
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	if (s->s3->client_opaque_prf_input != NULL)
+		OPENSSL_free(s->s3->client_opaque_prf_input);
+	s->s3->client_opaque_prf_input = NULL;
+	if (s->s3->server_opaque_prf_input != NULL)
+		OPENSSL_free(s->s3->server_opaque_prf_input);
+	s->s3->server_opaque_prf_input = NULL;
+#endif
+
 	ssl3_cleanup_key_block(s);
 	if (s->s3->tmp.ca_names != NULL)
 		sk_X509_NAME_pop_free(s->s3->tmp.ca_names,X509_NAME_free);
@@ -2337,11 +2353,33 @@
 			SSLerr(SSL_F_SSL3_CTRL, SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE);
 			return 0;
 			}
-		s->options |= SSL_OP_NO_SSLv2; /* can't use extension w/ SSL 2.0 format */
  		break;
 	case SSL_CTRL_SET_TLSEXT_DEBUG_ARG:
 		s->tlsext_debug_arg=parg;
+		ret = 1;
 		break;
+
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	case SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT:
+		if (larg > 12288) /* actual internal limit is 2^16 for the complete hello message
+		                   * (including the cert chain and everything) */
+			{
+			SSLerr(SSL_F_SSL3_CTRL, SSL_R_OPAQUE_PRF_INPUT_TOO_LONG);
+			break;
+			}
+		if (s->tlsext_opaque_prf_input != NULL)
+			OPENSSL_free(s->tlsext_opaque_prf_input);
+		s->tlsext_opaque_prf_input = BUF_memdup(parg, (size_t)larg);
+		if (s->tlsext_opaque_prf_input != NULL)
+			{
+			s->tlsext_opaque_prf_input_len = (size_t)larg;
+			ret = 1;
+			}
+		else
+			s->tlsext_opaque_prf_input_len = 0;
+		break;
+#endif
+
 #endif /* !OPENSSL_NO_TLSEXT */
 	default:
 		break;
@@ -2562,7 +2600,15 @@
 			}
 		return 1;
 		}
+
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	case SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT_CB_ARG:
+		ctx->tlsext_opaque_prf_input_callback_arg = parg;
+		return 1;
+#endif
+
 #endif /* !OPENSSL_NO_TLSEXT */
+
 	/* A Thawte special :-) */
 	case SSL_CTRL_EXTRA_CHAIN_CERT:
 		if (ctx->extra_certs == NULL)
@@ -2612,6 +2658,13 @@
 	case SSL_CTRL_SET_TLSEXT_SERVERNAME_CB:
 		ctx->tlsext_servername_callback=(int (*)(SSL *,int *,void *))fp;
 		break;
+
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	case SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT_CB:
+		ctx->tlsext_opaque_prf_input_callback = (int (*)(SSL *,void *, size_t, void *))fp;
+		break;
+#endif
+
 #endif
 	default:
 		return(0);
diff --git a/ssl/ssl.h b/ssl/ssl.h
index a2669f9..ec42280 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -799,7 +799,12 @@
 	unsigned char tlsext_tick_key_name[16];
 	unsigned char tlsext_tick_hmac_key[16];
 	unsigned char tlsext_tick_aes_key[16];
+
+	/* draft-rescorla-tls-opaque-prf-input-00.txt information */
+	int (*tlsext_opaque_prf_input_callback)(SSL *, void *peerinput, size_t len, void *arg);
+	void *tlsext_opaque_prf_input_callback_arg;
 #endif
+
 #ifndef OPENSSL_NO_PSK
 	char *psk_identity_hint;
 	unsigned int (*psk_client_callback)(SSL *ssl, const char *hint, char *identity,
@@ -1086,11 +1091,16 @@
 	size_t tlsext_ellipticcurvelist_length;
 	unsigned char *tlsext_ellipticcurvelist; /* our list */
 #endif /* OPENSSL_NO_EC */
+
+	/* draft-rescorla-tls-opaque-prf-input-00.txt information to be used for handshakes */
+	void *tlsext_opaque_prf_input;
+	size_t tlsext_opaque_prf_input_len;
+
 	SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */
 #define session_ctx initial_ctx
 #else
 #define session_ctx ctx
-#endif
+#endif /* OPENSSL_NO_TLSEXT */
 	};
 
 #ifdef __cplusplus
@@ -1304,6 +1314,9 @@
 #define SSL_CTRL_SET_TLSEXT_DEBUG_ARG		57
 #define SSL_CTRL_GET_TLSEXT_TICKET_KEYS		58
 #define SSL_CTRL_SET_TLSEXT_TICKET_KEYS		59
+#define SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT	60
+#define SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT_CB	61
+#define SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT_CB_ARG 62
 #endif
 
 #define SSL_session_reused(ssl) \
@@ -2009,6 +2022,7 @@
 #define SSL_R_NULL_SSL_METHOD_PASSED			 196
 #define SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED		 197
 #define SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE		 297
+#define SSL_R_OPAQUE_PRF_INPUT_TOO_LONG			 327
 #define SSL_R_PACKET_LENGTH_TOO_LONG			 198
 #define SSL_R_PARSE_TLSEXT				 227
 #define SSL_R_PATH_TOO_LONG				 270
diff --git a/ssl/ssl3.h b/ssl/ssl3.h
index 0543cb2..29cd3da 100644
--- a/ssl/ssl3.h
+++ b/ssl/ssl3.h
@@ -443,6 +443,14 @@
 
 	int in_read_app_data;
 
+	/* Opaque PRF input as used for the current handshake.
+	 * These fields are used only if TLSEXT_TYPE_opaque_prf_input is defined
+	 * (otherwise, they are merely present to improve binary compatibility) */
+	void *client_opaque_prf_input;
+	size_t client_opaque_prf_input_len;
+	void *server_opaque_prf_input;
+	size_t server_opaque_prf_input_len;
+
 	struct	{
 		/* actually only needs to be 16+20 */
 		unsigned char cert_verify_md[EVP_MAX_MD_SIZE*2];
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 18a64e9..3121e0e 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -249,13 +249,13 @@
 {ERR_FUNC(SSL_F_SSL_USE_RSAPRIVATEKEY_FILE),	"SSL_use_RSAPrivateKey_file"},
 {ERR_FUNC(SSL_F_SSL_VERIFY_CERT_CHAIN),	"SSL_VERIFY_CERT_CHAIN"},
 {ERR_FUNC(SSL_F_SSL_WRITE),	"SSL_write"},
-{ERR_FUNC(SSL_F_TLS1_CERT_VERIFY_MAC),	"tls1_cert_verify_mac"},
+{ERR_FUNC(SSL_F_TLS1_CERT_VERIFY_MAC),	"TLS1_CERT_VERIFY_MAC"},
 {ERR_FUNC(SSL_F_TLS1_CHANGE_CIPHER_STATE),	"TLS1_CHANGE_CIPHER_STATE"},
 {ERR_FUNC(SSL_F_TLS1_CHECK_SERVERHELLO_TLSEXT),	"TLS1_CHECK_SERVERHELLO_TLSEXT"},
 {ERR_FUNC(SSL_F_TLS1_ENC),	"TLS1_ENC"},
 {ERR_FUNC(SSL_F_TLS1_PREPARE_CLIENTHELLO_TLSEXT),	"TLS1_PREPARE_CLIENTHELLO_TLSEXT"},
 {ERR_FUNC(SSL_F_TLS1_PREPARE_SERVERHELLO_TLSEXT),	"TLS1_PREPARE_SERVERHELLO_TLSEXT"},
-{ERR_FUNC(SSL_F_TLS1_PRF),	"tls1_prf"},
+{ERR_FUNC(SSL_F_TLS1_PRF),	"TLS1_PRF"},
 {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK),	"TLS1_SETUP_KEY_BLOCK"},
 {ERR_FUNC(SSL_F_WRITE_PENDING),	"WRITE_PENDING"},
 {0,NULL}
@@ -399,6 +399,7 @@
 {ERR_REASON(SSL_R_NULL_SSL_METHOD_PASSED),"null ssl method passed"},
 {ERR_REASON(SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED),"old session cipher not returned"},
 {ERR_REASON(SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE),"only tls allowed in fips mode"},
+{ERR_REASON(SSL_R_OPAQUE_PRF_INPUT_TOO_LONG),"opaque PRF input too long"},
 {ERR_REASON(SSL_R_PACKET_LENGTH_TOO_LONG),"packet length too long"},
 {ERR_REASON(SSL_R_PARSE_TLSEXT)          ,"parse tlsext"},
 {ERR_REASON(SSL_R_PATH_TOO_LONG)         ,"path too long"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 575cf7c..00a93ee 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -542,6 +542,7 @@
 	if (s->tlsext_ecpointformatlist) OPENSSL_free(s->tlsext_ecpointformatlist);
 	if (s->tlsext_ellipticcurvelist) OPENSSL_free(s->tlsext_ellipticcurvelist);
 #endif /* OPENSSL_NO_EC */
+	if (s->tlsext_opaque_prf_input) OPENSSL_free(s->tlsext_opaque_prf_input);
 #endif
 
 	if (s->client_CA != NULL)
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 2f8f0f8..50a8acc 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -330,24 +330,30 @@
 #define SSL_SSLV3		0x00000002L
 #define SSL_TLSV1		SSL_SSLV3	/* for now */
 
-/* Bits for algorithm2 (handshake digests) */
+
+/* Bits for algorithm2 (handshake digests and other extra flags) */
 
 #define SSL_HANDSHAKE_MAC_MD5 0x10
 #define SSL_HANDSHAKE_MAC_SHA 0x20
 #define SSL_HANDSHAKE_MAC_GOST94 0x40
 #define SSL_HANDSHAKE_MAC_DEFAULT (SSL_HANDSHAKE_MAC_MD5 | SSL_HANDSHAKE_MAC_SHA)
 
-
 /* When adding new digest in the ssl_ciph.c and increment SSM_MD_NUM_IDX
  * make sure to update this constant too */
 #define SSL_MAX_DIGEST 4
 
-
 #define TLS1_PRF_DGST_SHIFT 8
 #define TLS1_PRF_MD5 (SSL_HANDSHAKE_MAC_MD5 << TLS1_PRF_DGST_SHIFT)
 #define TLS1_PRF_SHA1 (SSL_HANDSHAKE_MAC_SHA << TLS1_PRF_DGST_SHIFT)
 #define TLS1_PRF_GOST94 (SSL_HANDSHAKE_MAC_GOST94 << TLS1_PRF_DGST_SHIFT)
 #define TLS1_PRF (TLS1_PRF_MD5 | TLS1_PRF_SHA1)
+
+/* Stream MAC for GOST ciphersuites from cryptopro draft
+ * (currently this also goes into algorithm2) */
+#define TLS1_STREAM_MAC 0x04
+
+
+
 /*
  * Export and cipher strength information. For each cipher we have to decide
  * whether it is exportable or not. This information is likely to change
diff --git a/ssl/ssltest.c b/ssl/ssltest.c
index 0be921f..83a571d 100644
--- a/ssl/ssltest.c
+++ b/ssl/ssltest.c
@@ -422,6 +422,25 @@
 		}
 	}
 
+#ifdef TLSEXT_TYPE_opaque_prf_input
+struct cb_info_st { void *input; size_t len; int ret; };
+struct cb_info_st co1 = { "C", 1, 1 }; /* try to negotiate oqaque PRF input */
+struct cb_info_st co2 = { "C", 1, 2 }; /* insist on oqaque PRF input */
+struct cb_info_st so1 = { "S", 1, 1 }; /* try to negotiate oqaque PRF input */
+struct cb_info_st so2 = { "S", 1, 2 }; /* insist on oqaque PRF input */
+
+int opaque_prf_input_cb(SSL *ssl, void *peerinput, size_t len, void *arg_)
+	{
+	struct cb_info_st *arg = arg_;
+
+	if (arg == NULL)
+		return 1;
+	
+	if (!SSL_set_tlsext_opaque_prf_input(ssl, arg->input, arg->len))
+		return 0;
+	return arg->ret;
+	}
+#endif
 
 int main(int argc, char *argv[])
 	{
@@ -836,6 +855,13 @@
 	SSL_CTX_set_tmp_rsa_callback(s_ctx,tmp_rsa_cb);
 #endif
 
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	SSL_CTX_set_tlsext_opaque_prf_input_callback(c_ctx, opaque_prf_input_cb);
+	SSL_CTX_set_tlsext_opaque_prf_input_callback(s_ctx, opaque_prf_input_cb);
+	SSL_CTX_set_tlsext_opaque_prf_input_callback_arg(c_ctx, &co1); /* or &co2 or NULL */
+	SSL_CTX_set_tlsext_opaque_prf_input_callback_arg(s_ctx, &so1); /* or &so2 or NULL */
+#endif
+
 	if (!SSL_CTX_use_certificate_file(s_ctx,server_cert,SSL_FILETYPE_PEM))
 		{
 		ERR_print_errors(bio_err);
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index 80cfe44..3509f62 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -142,8 +142,14 @@
 #include <openssl/hmac.h>
 #include <openssl/md5.h>
 
+/* seed1 through seed5 are virtually concatenated */
 static void tls1_P_hash(const EVP_MD *md, const unsigned char *sec,
-			int sec_len, unsigned char *seed, int seed_len,
+			int sec_len,
+			const void *seed1, int seed1_len,
+			const void *seed2, int seed2_len,
+			const void *seed3, int seed3_len,
+			const void *seed4, int seed4_len,
+			const void *seed5, int seed5_len,
 			unsigned char *out, int olen)
 	{
 	int chunk,n;
@@ -159,7 +165,11 @@
 	HMAC_CTX_init(&ctx_tmp);
 	HMAC_Init_ex(&ctx,sec,sec_len,md, NULL);
 	HMAC_Init_ex(&ctx_tmp,sec,sec_len,md, NULL);
-	HMAC_Update(&ctx,seed,seed_len);
+	if (seed1 != NULL) HMAC_Update(&ctx,seed1,seed1_len);
+	if (seed2 != NULL) HMAC_Update(&ctx,seed2,seed2_len);
+	if (seed3 != NULL) HMAC_Update(&ctx,seed3,seed3_len);
+	if (seed4 != NULL) HMAC_Update(&ctx,seed4,seed4_len);
+	if (seed5 != NULL) HMAC_Update(&ctx,seed5,seed5_len);
 	HMAC_Final(&ctx,A1,&A1_len);
 
 	n=0;
@@ -169,7 +179,11 @@
 		HMAC_Init_ex(&ctx_tmp,NULL,0,NULL,NULL); /* re-init */
 		HMAC_Update(&ctx,A1,A1_len);
 		HMAC_Update(&ctx_tmp,A1,A1_len);
-		HMAC_Update(&ctx,seed,seed_len);
+		if (seed1 != NULL) HMAC_Update(&ctx,seed1,seed1_len);
+		if (seed2 != NULL) HMAC_Update(&ctx,seed2,seed2_len);
+		if (seed3 != NULL) HMAC_Update(&ctx,seed3,seed3_len);
+		if (seed4 != NULL) HMAC_Update(&ctx,seed4,seed4_len);
+		if (seed5 != NULL) HMAC_Update(&ctx,seed5,seed5_len);
 
 		if (olen > chunk)
 			{
@@ -190,9 +204,15 @@
 	OPENSSL_cleanse(A1,sizeof(A1));
 	}
 
+/* seed1 through seed5 are virtually concatenated */
 static void tls1_PRF(long digest_mask,
-		     unsigned char *label, int label_len,
-		     const unsigned char *sec, int slen, unsigned char *out1,
+		     const void *seed1, int seed1_len,
+		     const void *seed2, int seed2_len,
+		     const void *seed3, int seed3_len,
+		     const void *seed4, int seed4_len,
+		     const void *seed5, int seed5_len,
+		     const unsigned char *sec, int slen,
+		     unsigned char *out1,
 		     unsigned char *out2, int olen)
 	{
 	int len,i,idx,count;
@@ -200,7 +220,7 @@
 	long m;
 	const EVP_MD *md;
 
-	/* Count number of digests and divide sec evenly */
+	/* Count number of digests and partition sec evenly */
 	count=0;
 	for (idx=0;ssl_get_handshake_digest(idx,&m,&md);idx++) {
 		if ((m<<TLS1_PRF_DGST_SHIFT) & digest_mask) count++;
@@ -215,7 +235,9 @@
 				SSL_R_UNSUPPORTED_DIGEST_TYPE);
 				return;				
 			}
-			tls1_P_hash(md ,S1,len+(slen&1),label,label_len,out2,olen);
+			tls1_P_hash(md ,S1,len+(slen&1),
+			            seed1,seed1_len,seed2,seed2_len,seed3,seed3_len,seed4,seed4_len,seed5,seed5_len,
+			            out2,olen);
 			S1+=len;
 			for (i=0; i<olen; i++)
 			{
@@ -228,20 +250,11 @@
 static void tls1_generate_key_block(SSL *s, unsigned char *km,
 	     unsigned char *tmp, int num)
 	{
-	unsigned char *p;
-	unsigned char buf[SSL3_RANDOM_SIZE*2+
-		TLS_MD_MAX_CONST_SIZE];
-	p=buf;
-
-	memcpy(p,TLS_MD_KEY_EXPANSION_CONST,
-		TLS_MD_KEY_EXPANSION_CONST_SIZE);
-	p+=TLS_MD_KEY_EXPANSION_CONST_SIZE;
-	memcpy(p,s->s3->server_random,SSL3_RANDOM_SIZE);
-	p+=SSL3_RANDOM_SIZE;
-	memcpy(p,s->s3->client_random,SSL3_RANDOM_SIZE);
-	p+=SSL3_RANDOM_SIZE;
-
-	tls1_PRF(s->s3->tmp.new_cipher->algorithm2,buf,(int)(p-buf),
+	tls1_PRF(s->s3->tmp.new_cipher->algorithm2,
+		 TLS_MD_KEY_EXPANSION_CONST,TLS_MD_KEY_EXPANSION_CONST_SIZE,
+		 s->s3->server_random,SSL3_RANDOM_SIZE,
+		 s->s3->client_random,SSL3_RANDOM_SIZE,
+		 NULL,0,NULL,0,
 		 s->session->master_key,s->session->master_key_length,
 		 km,tmp,num);
 #ifdef KSSL_DEBUG
@@ -261,8 +274,7 @@
 	{
 	static const unsigned char empty[]="";
 	unsigned char *p,*key_block,*mac_secret;
-	unsigned char *exp_label,buf[TLS_MD_MAX_CONST_SIZE+
-		SSL3_RANDOM_SIZE*2];
+	unsigned char *exp_label;
 	unsigned char tmp1[EVP_MAX_KEY_LENGTH];
 	unsigned char tmp2[EVP_MAX_KEY_LENGTH];
 	unsigned char iv1[EVP_MAX_IV_LENGTH*2];
@@ -443,29 +455,22 @@
 		/* In here I set both the read and write key/iv to the
 		 * same value since only the correct one will be used :-).
 		 */
-		p=buf;
-		memcpy(p,exp_label,exp_label_len);
-		p+=exp_label_len;
-		memcpy(p,s->s3->client_random,SSL3_RANDOM_SIZE);
-		p+=SSL3_RANDOM_SIZE;
-		memcpy(p,s->s3->server_random,SSL3_RANDOM_SIZE);
-		p+=SSL3_RANDOM_SIZE;
-		tls1_PRF(s->s3->tmp.new_cipher->algorithm2,buf,(int)(p-buf),key,j,
-			 tmp1,tmp2,EVP_CIPHER_key_length(c));
+		tls1_PRF(s->s3->tmp.new_cipher->algorithm2,
+			 exp_label,exp_label_len,
+			 s->s3->client_random,SSL3_RANDOM_SIZE,
+			 s->s3->server_random,SSL3_RANDOM_SIZE,
+			 NULL,0,NULL,0,
+			 key,j,tmp1,tmp2,EVP_CIPHER_key_length(c));
 		key=tmp1;
 
 		if (k > 0)
 			{
-			p=buf;
-			memcpy(p,TLS_MD_IV_BLOCK_CONST,
-				TLS_MD_IV_BLOCK_CONST_SIZE);
-			p+=TLS_MD_IV_BLOCK_CONST_SIZE;
-			memcpy(p,s->s3->client_random,SSL3_RANDOM_SIZE);
-			p+=SSL3_RANDOM_SIZE;
-			memcpy(p,s->s3->server_random,SSL3_RANDOM_SIZE);
-			p+=SSL3_RANDOM_SIZE;
-			tls1_PRF(s->s3->tmp.new_cipher->algorithm2,buf,p-buf,empty,0,
-				 iv1,iv2,k*2);
+			tls1_PRF(s->s3->tmp.new_cipher->algorithm2,
+				 TLS_MD_IV_BLOCK_CONST,TLS_MD_IV_BLOCK_CONST_SIZE,
+				 s->s3->client_random,SSL3_RANDOM_SIZE,
+				 s->s3->server_random,SSL3_RANDOM_SIZE,
+				 NULL,0,NULL,0,
+				 empty,0,iv1,iv2,k*2);
 			if (client_write)
 				iv=iv1;
 			else
@@ -767,35 +772,51 @@
 	{
 	unsigned int i;
 	EVP_MD_CTX ctx;
-	unsigned char buf[TLS_MD_MAX_CONST_SIZE+MD5_DIGEST_LENGTH+SHA_DIGEST_LENGTH];
+	unsigned char buf[2*EVP_MAX_MD_SIZE];
 	unsigned char *q,buf2[12];
 	int idx;
 	long mask;
+	int err=0;
 	const EVP_MD *md; 
 
 	q=buf;
-	memcpy(q,str,slen);
-	q+=slen;
 
 	EVP_MD_CTX_init(&ctx);
 
 	if (s->s3->handshake_buffer) 
 		ssl3_digest_cached_records(s);
 
-	for (idx=0;ssl_get_handshake_digest(idx,&mask,&md);idx++) {
-		if (mask & s->s3->tmp.new_cipher->algorithm2) {
-			EVP_MD_CTX_copy_ex(&ctx,s->s3->handshake_dgst[idx]);
-			EVP_DigestFinal_ex(&ctx,q,&i);
-			q+=i;
+	for (idx=0;ssl_get_handshake_digest(idx,&mask,&md);idx++)
+		{
+		if (mask & s->s3->tmp.new_cipher->algorithm2)
+			{
+			int hashsize = EVP_MD_size(md);
+			if ((size_t)hashsize > (sizeof buf - (size_t)(q-buf)))
+				{
+				/* internal error: 'buf' is too small for this cipersuite! */
+				err = 1;
+				}
+			else
+				{
+				EVP_MD_CTX_copy_ex(&ctx,s->s3->handshake_dgst[idx]);
+				EVP_DigestFinal_ex(&ctx,q,&i);
+				if (i != hashsize) /* can't really happen */
+					err = 1;
+				q+=i;
+				}
+			}
 		}
-	}
-
-	tls1_PRF(s->s3->tmp.new_cipher->algorithm2,buf,(int)(q-buf),
-		s->session->master_key,s->session->master_key_length,
-		out,buf2,sizeof buf2);
+		
+	tls1_PRF(s->s3->tmp.new_cipher->algorithm2,
+		 str,slen, buf,(int)(q-buf), NULL,0, NULL,0, NULL,0,
+		 s->session->master_key,s->session->master_key_length,
+		 out,buf2,sizeof buf2);
 	EVP_MD_CTX_cleanup(&ctx);
 
-	return sizeof buf2;
+	if (err)
+		return 0;
+	else
+		return sizeof buf2;
 	}
 
 int tls1_mac(SSL *ssl, unsigned char *md, int send)
@@ -876,23 +897,35 @@
 int tls1_generate_master_secret(SSL *s, unsigned char *out, unsigned char *p,
 	     int len)
 	{
-	unsigned char buf[SSL3_RANDOM_SIZE*2+TLS_MD_MASTER_SECRET_CONST_SIZE];
 	unsigned char buff[SSL_MAX_MASTER_KEY_LENGTH];
+	const void *co = NULL, *so = NULL;
+	int col = 0, sol = NULL;
 
 #ifdef KSSL_DEBUG
 	printf ("tls1_generate_master_secret(%p,%p, %p, %d)\n", s,out, p,len);
 #endif	/* KSSL_DEBUG */
 
-	/* Setup the stuff to munge */
-	memcpy(buf,TLS_MD_MASTER_SECRET_CONST,
-		TLS_MD_MASTER_SECRET_CONST_SIZE);
-	memcpy(&(buf[TLS_MD_MASTER_SECRET_CONST_SIZE]),
-		s->s3->client_random,SSL3_RANDOM_SIZE);
-	memcpy(&(buf[SSL3_RANDOM_SIZE+TLS_MD_MASTER_SECRET_CONST_SIZE]),
-		s->s3->server_random,SSL3_RANDOM_SIZE);
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	if (s->s3->client_opaque_prf_input != NULL && s->s3->server_opaque_prf_input != NULL &&
+	    s->s3->client_opaque_prf_input_len > 0 &&
+	    s->s3->client_opaque_prf_input_len == s->s3->server_opaque_prf_input_len)
+		{
+		co = s->s3->client_opaque_prf_input;
+		col = s->s3->server_opaque_prf_input_len;
+		so = s->s3->server_opaque_prf_input;
+		sol = s->s3->client_opaque_prf_input_len; /* must be same as col (see draft-rescorla-tls-opaque-prf-input-00.txt, section 3.1) */
+		}
+#endif
+
 	tls1_PRF(s->s3->tmp.new_cipher->algorithm2,
-		buf,TLS_MD_MASTER_SECRET_CONST_SIZE+SSL3_RANDOM_SIZE*2,p,len,
+		TLS_MD_MASTER_SECRET_CONST,TLS_MD_MASTER_SECRET_CONST_SIZE,
+		s->s3->client_random,SSL3_RANDOM_SIZE,
+		co, col,
+		s->s3->server_random,SSL3_RANDOM_SIZE,
+		so, sol,
+		p,len,
 		s->session->master_key,buff,sizeof buff);
+
 #ifdef KSSL_DEBUG
 	printf ("tls1_generate_master_secret() complete\n");
 #endif	/* KSSL_DEBUG */
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index b5eab2c..0c78414 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -284,8 +284,8 @@
 		   + hostname length 
 		*/
 		   
-		if ((lenmax = limit - p - 9) < 0 
-		|| (size_str = strlen(s->tlsext_hostname)) > (unsigned long)lenmax) 
+		if ((lenmax = limit - ret - 9) < 0 
+		    || (size_str = strlen(s->tlsext_hostname)) > (unsigned long)lenmax) 
 			return NULL;
 			
 		/* extension type and length */
@@ -300,15 +300,15 @@
 		s2n(size_str,ret);
 		memcpy(ret, s->tlsext_hostname, size_str);
 		ret+=size_str;
-
 		}
+
 #ifndef OPENSSL_NO_EC
 	if (s->tlsext_ecpointformatlist != NULL)
 		{
 		/* Add TLS extension ECPointFormats to the ClientHello message */
 		long lenmax; 
 
-		if ((lenmax = limit - p - 5) < 0) return NULL; 
+		if ((lenmax = limit - ret - 5) < 0) return NULL; 
 		if (s->tlsext_ecpointformatlist_length > (unsigned long)lenmax) return NULL;
 		if (s->tlsext_ecpointformatlist_length > 255)
 			{
@@ -327,7 +327,7 @@
 		/* Add TLS extension EllipticCurves to the ClientHello message */
 		long lenmax; 
 
-		if ((lenmax = limit - p - 6) < 0) return NULL; 
+		if ((lenmax = limit - ret - 6) < 0) return NULL; 
 		if (s->tlsext_ellipticcurvelist_length > (unsigned long)lenmax) return NULL;
 		if (s->tlsext_ellipticcurvelist_length > 65532)
 			{
@@ -359,8 +359,7 @@
 		/* Check for enough room 2 for extension type, 2 for len
  		 * rest for ticket
   		 */
-		if (limit - p - 4 - ticklen < 0)
-			return NULL;
+		if ((long)(limit - ret - 4 - ticklen) < 0) return NULL;
 		s2n(TLSEXT_TYPE_session_ticket,ret); 
 		s2n(ticklen,ret);
 		if (ticklen)
@@ -370,6 +369,24 @@
 			}
 		}
 
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	if (s->s3->client_opaque_prf_input != NULL)
+		{
+		size_t col = s->s3->client_opaque_prf_input_len;
+		
+		if ((long)(limit - ret - 6 - col < 0))
+			return NULL;
+		if (col > 0xFFFD) /* can't happen */
+			return NULL;
+
+		s2n(TLSEXT_TYPE_opaque_prf_input, ret); 
+		s2n(col + 2, ret);
+		s2n(col, ret);
+		memcpy(ret, s->s3->client_opaque_prf_input, col);
+		ret += col;
+		}
+#endif
+
 	if ((extdatalen = ret-p-2)== 0) 
 		return p;
 
@@ -387,7 +404,7 @@
 
 	if (!s->hit && s->servername_done == 1 && s->session->tlsext_hostname != NULL)
 		{ 
-		if (limit - p - 4 < 0) return NULL; 
+		if ((long)(limit - ret - 4) < 0) return NULL; 
 
 		s2n(TLSEXT_TYPE_server_name,ret);
 		s2n(0,ret);
@@ -398,7 +415,7 @@
 		/* Add TLS extension ECPointFormats to the ServerHello message */
 		long lenmax; 
 
-		if ((lenmax = limit - p - 5) < 0) return NULL; 
+		if ((lenmax = limit - ret - 5) < 0) return NULL; 
 		if (s->tlsext_ecpointformatlist_length > (unsigned long)lenmax) return NULL;
 		if (s->tlsext_ecpointformatlist_length > 255)
 			{
@@ -419,11 +436,29 @@
 	if (s->tlsext_ticket_expected
 		&& !(SSL_get_options(s) & SSL_OP_NO_TICKET)) 
 		{ 
-		if (limit - p - 4 < 0) return NULL; 
+		if ((long)(limit - ret - 4) < 0) return NULL; 
 		s2n(TLSEXT_TYPE_session_ticket,ret);
 		s2n(0,ret);
 		}
+
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	if (s->s3->server_opaque_prf_input != NULL)
+		{
+		size_t sol = s->s3->server_opaque_prf_input_len;
 		
+		if ((long)(limit - ret - 6 - sol) < 0)
+			return NULL;
+		if (sol > 0xFFFD) /* can't happen */
+			return NULL;
+
+		s2n(TLSEXT_TYPE_opaque_prf_input, ret); 
+		s2n(sol + 2, ret);
+		s2n(sol, ret);
+		memcpy(ret, s->s3->server_opaque_prf_input, sol);
+		ret += sol;
+		}
+#endif
+
 	if ((extdatalen = ret-p-2)== 0) 
 		return p;
 
@@ -610,6 +645,35 @@
 #endif
 			}
 #endif /* OPENSSL_NO_EC */
+#ifdef TLSEXT_TYPE_opaque_prf_input
+		else if (type == TLSEXT_TYPE_opaque_prf_input)
+			{
+			unsigned char *sdata = data;
+
+			if (size < 2)
+				{
+				*al = SSL_AD_DECODE_ERROR;
+				return 0;
+				}
+			n2s(sdata, s->s3->client_opaque_prf_input_len);
+			if (s->s3->client_opaque_prf_input_len != size - 2)
+				{
+				*al = SSL_AD_DECODE_ERROR;
+				return 0;
+				}
+
+			if (s->s3->client_opaque_prf_input != NULL) /* shouldn't really happen */
+				OPENSSL_free(s->s3->client_opaque_prf_input);
+
+			s->s3->client_opaque_prf_input = BUF_memdup(sdata, s->s3->client_opaque_prf_input_len);
+			if (s->s3->client_opaque_prf_input == NULL)
+				{
+				*al = TLS1_AD_INTERNAL_ERROR;
+				return 0;
+				}
+			}
+#endif
+
 		/* session ticket processed earlier */
 		data+=size;
 		}
@@ -694,6 +758,35 @@
 				}
 			s->tlsext_ticket_expected = 1;
 			}
+#ifdef TLSEXT_TYPE_opaque_prf_input
+		else if (type == TLSEXT_TYPE_opaque_prf_input)
+			{
+			unsigned char *sdata = data;
+
+			if (size < 2)
+				{
+				*al = SSL_AD_DECODE_ERROR;
+				return 0;
+				}
+			n2s(sdata, s->s3->server_opaque_prf_input_len);
+			if (s->s3->server_opaque_prf_input_len != size - 2)
+				{
+				*al = SSL_AD_DECODE_ERROR;
+				return 0;
+				}
+			
+			if (s->s3->server_opaque_prf_input != NULL) /* shouldn't really happen */
+				OPENSSL_free(s->s3->server_opaque_prf_input);
+			s->s3->server_opaque_prf_input = BUF_memdup(sdata, s->s3->server_opaque_prf_input_len);
+
+			if (s->s3->server_opaque_prf_input == NULL)
+				{
+				*al = TLS1_AD_INTERNAL_ERROR;
+				return 0;
+				}
+			}
+#endif
+
 		data+=size;		
 		}
 
@@ -780,6 +873,38 @@
 			s2n(i,j);
 		}
 #endif /* OPENSSL_NO_EC */
+
+#ifdef TLSEXT_TYPE_opaque_prf_input
+ 	{
+		int r = 1;
+	
+		if (s->ctx->tlsext_opaque_prf_input_callback != 0)
+			{
+			r = s->ctx->tlsext_opaque_prf_input_callback(s, NULL, 0, s->ctx->tlsext_opaque_prf_input_callback_arg);
+			if (!r)
+				return -1;
+			}
+
+		if (s->tlsext_opaque_prf_input != NULL)
+			{
+			if (s->s3->client_opaque_prf_input != NULL) /* shouldn't really happen */
+				OPENSSL_free(s->s3->client_opaque_prf_input);
+
+			s->s3->client_opaque_prf_input = BUF_memdup(s->tlsext_opaque_prf_input, s->tlsext_opaque_prf_input_len);
+			if (s->s3->client_opaque_prf_input == NULL)
+				{
+				SSLerr(SSL_F_SSL_PREPARE_CLIENTHELLO_TLSEXT,ERR_R_MALLOC_FAILURE);
+				return -1;
+				}
+			s->s3->client_opaque_prf_input_len = s->tlsext_opaque_prf_input_len;
+			}
+
+		if (r == 2)
+			/* at callback's request, insist on receiving an appropriate server opaque PRF input */
+			s->s3->server_opaque_prf_input_len = s->tlsext_opaque_prf_input_len;
+	}
+#endif
+
 	return 1;
 	}
 
@@ -810,6 +935,7 @@
 		s->tlsext_ecpointformatlist[2] = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2;
 		}
 #endif /* OPENSSL_NO_EC */
+
 	return 1;
 	}
 
@@ -832,6 +958,62 @@
 	else if (s->initial_ctx != NULL && s->initial_ctx->tlsext_servername_callback != 0) 		
 		ret = s->initial_ctx->tlsext_servername_callback(s, &al, s->initial_ctx->tlsext_servername_arg);
 
+
+#ifdef TLSEXT_TYPE_opaque_prf_input
+ 	{
+		/* This sort of belongs into ssl_prepare_serverhello_tlsext(),
+		 * but we might be sending an alert in response to the client hello,
+		 * so this has to happen here in ssl_check_clienthello_tlsext(). */
+
+		int r = 1;
+	
+		if (s->ctx->tlsext_opaque_prf_input_callback != 0)
+			{
+			r = s->ctx->tlsext_opaque_prf_input_callback(s, NULL, 0, s->ctx->tlsext_opaque_prf_input_callback_arg);
+			if (!r)
+				{
+				ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+				al = SSL_AD_INTERNAL_ERROR;
+				goto err;
+				}
+			}
+
+		if (s->s3->server_opaque_prf_input != NULL) /* shouldn't really happen */
+			OPENSSL_free(s->s3->server_opaque_prf_input);
+		s->s3->server_opaque_prf_input = NULL;
+
+		if (s->tlsext_opaque_prf_input != NULL)
+			{
+			if (s->s3->client_opaque_prf_input != NULL &&
+				s->s3->client_opaque_prf_input_len == s->tlsext_opaque_prf_input_len)
+				{
+				/* can only use this extension if we have a server opaque PRF input
+				 * of the same length as the client opaque PRF input! */
+
+				s->s3->server_opaque_prf_input = BUF_memdup(s->tlsext_opaque_prf_input, s->tlsext_opaque_prf_input_len);
+				if (s->s3->server_opaque_prf_input == NULL)
+					{
+					ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+					al = SSL_AD_INTERNAL_ERROR;
+					goto err;
+					}
+				s->s3->server_opaque_prf_input_len = s->tlsext_opaque_prf_input_len;
+				}
+			}
+
+		if (r == 2 && s->s3->server_opaque_prf_input == NULL)
+			{
+			/* The callback wants to enforce use of the extension,
+			 * but we can't do that with the client opaque PRF input;
+			 * abort the handshake.
+			 */
+			ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+			al = SSL_AD_HANDSHAKE_FAILURE;
+			}
+	}
+#endif
+
+ err:
 	switch (ret)
 		{
 		case SSL_TLSEXT_ERR_ALERT_FATAL:
@@ -895,6 +1077,29 @@
 	else if (s->initial_ctx != NULL && s->initial_ctx->tlsext_servername_callback != 0) 		
 		ret = s->initial_ctx->tlsext_servername_callback(s, &al, s->initial_ctx->tlsext_servername_arg);
 
+#ifdef TLSEXT_TYPE_opaque_prf_input
+	if (s->s3->server_opaque_prf_input_len > 0)
+		{
+		/* This case may indicate that we, as a client, want to insist on using opaque PRF inputs.
+		 * So first verify that we really have a value from the server too. */
+
+		if (s->s3->server_opaque_prf_input == NULL)
+			{
+			ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+			al = SSL_AD_HANDSHAKE_FAILURE;
+			}
+		
+		/* Anytime the server *has* sent an opaque PRF input, we need to check
+		 * that we have a client opaque PRF input of the same size. */
+		if (s->s3->client_opaque_prf_input == NULL ||
+		    s->s3->client_opaque_prf_input_len != s->s3->server_opaque_prf_input_len)
+			{
+			ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+			al = SSL_AD_ILLEGAL_PARAMETER;
+			}
+		}
+#endif
+
 	switch (ret)
 		{
 		case SSL_TLSEXT_ERR_ALERT_FATAL:
diff --git a/ssl/tls1.h b/ssl/tls1.h
index f55ab3d..cac8d0e 100644
--- a/ssl/tls1.h
+++ b/ssl/tls1.h
@@ -183,16 +183,23 @@
 #define TLS1_AD_BAD_CERTIFICATE_HASH_VALUE 114
 #define TLS1_AD_UNKNOWN_PSK_IDENTITY	115	/* fatal */
 
-/* ExtensionType values from RFC 3546 */
+/* ExtensionType values from RFC3546 / RFC4366 */
 #define TLSEXT_TYPE_server_name			0
 #define TLSEXT_TYPE_max_fragment_length		1
 #define TLSEXT_TYPE_client_certificate_url	2
 #define TLSEXT_TYPE_trusted_ca_keys		3
 #define TLSEXT_TYPE_truncated_hmac		4
 #define TLSEXT_TYPE_status_request		5
+/* ExtensionType values from RFC4492 */
 #define TLSEXT_TYPE_elliptic_curves		10
 #define TLSEXT_TYPE_ec_point_formats		11
 #define TLSEXT_TYPE_session_ticket		35
+/* ExtensionType value from draft-rescorla-tls-opaque-prf-input-00.txt */
+#if 0 /* will have to be provided externally for now ,
+       * i.e. build with -DTLSEXT_TYPE_opaque_prf_input=38183
+       * using whatever extension number you'd like to try */
+# define TLSEXT_TYPE_opaque_prf_input		?? */
+#endif
 
 /* NameType value from RFC 3546 */
 #define TLSEXT_NAMETYPE_host_name 0
@@ -235,6 +242,14 @@
 	SSL_CTX_ctrl((ctx),SSL_CTRL_GET_TLXEXT_TICKET_KEYS,(keylen),(keys))
 #define SSL_CTX_set_tlsext_ticket_keys(ctx, keys, keylen) \
 	SSL_CTX_ctrl((ctx),SSL_CTRL_SET_TLXEXT_TICKET_KEYS,(keylen),(keys))
+
+#define SSL_set_tlsext_opaque_prf_input(s, src, len) \
+SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT, len, src)
+#define SSL_CTX_set_tlsext_opaque_prf_input_callback(ctx, cb) \
+SSL_CTX_callback_ctrl(ctx,SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT_CB, (void (*)(void))cb)
+#define SSL_CTX_set_tlsext_opaque_prf_input_callback_arg(ctx, arg) \
+SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT_CB_ARG, 0, arg)
+
 #endif
 
 /* PSK ciphersuites from 4279 */
@@ -416,10 +431,6 @@
 #define TLS1_TXT_DHE_RSA_WITH_SEED_SHA                  "DHE-RSA-SEED-SHA"
 #define TLS1_TXT_ADH_WITH_SEED_SHA                      "ADH-SEED-SHA"
 
-/* Flags for SSL_CIPHER.algorithm2 field */
-/* Stream MAC for GOST ciphersuites from cryptopro draft */
-#define TLS1_STREAM_MAC 0x04
-
 
 #define TLS_CT_RSA_SIGN			1
 #define TLS_CT_DSS_SIGN			2