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.
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";
}
#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:
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)
{
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)
{
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)
{
*(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);
}
#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);
s->s3->tmp.new_compression=comp;
}
#endif
+
#ifndef OPENSSL_NO_TLSEXT
/* TLS extensions*/
if (s->version > SSL3_VERSION)
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)
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);
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);
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);
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;
}
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)
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);
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,
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
#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) \
#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
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];
{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}
{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"},
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)
#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
}
}
+#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[])
{
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);
#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;
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;
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)
{
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;
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++;
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++)
{
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
{
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];
/* 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
{
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)
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 */
+ 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 */
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)
{
/* 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)
{
/* 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)
}
}
+#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;
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);
/* 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)
{
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;
#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;
}
}
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;
}
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;
}
s->tlsext_ecpointformatlist[2] = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2;
}
#endif /* OPENSSL_NO_EC */
+
return 1;
}
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:
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:
#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
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 */
#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