Index: openssl/ssl/d1_srvr.c |
=================================================================== |
--- openssl/ssl/d1_srvr.c (revision 105093) |
+++ openssl/ssl/d1_srvr.c (working copy) |
@@ -4,7 +4,7 @@ |
* (nagendra@cs.stanford.edu) for the OpenSSL project 2005. |
*/ |
/* ==================================================================== |
- * Copyright (c) 1999-2005 The OpenSSL Project. All rights reserved. |
+ * Copyright (c) 1999-2007 The OpenSSL Project. All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
@@ -121,14 +121,15 @@ |
#include <openssl/evp.h> |
#include <openssl/x509.h> |
#include <openssl/md5.h> |
+#include <openssl/bn.h> |
#ifndef OPENSSL_NO_DH |
#include <openssl/dh.h> |
#endif |
-static SSL_METHOD *dtls1_get_server_method(int ver); |
+static const SSL_METHOD *dtls1_get_server_method(int ver); |
static int dtls1_send_hello_verify_request(SSL *s); |
-static SSL_METHOD *dtls1_get_server_method(int ver) |
+static const SSL_METHOD *dtls1_get_server_method(int ver) |
{ |
if (ver == DTLS1_VERSION) |
return(DTLSv1_server_method()); |
@@ -144,10 +145,12 @@ |
int dtls1_accept(SSL *s) |
{ |
BUF_MEM *buf; |
- unsigned long l,Time=(unsigned long)time(NULL); |
+ unsigned long Time=(unsigned long)time(NULL); |
void (*cb)(const SSL *ssl,int type,int val)=NULL; |
+ unsigned long alg_k; |
int ret= -1; |
int new_state,state,skip=0; |
+ int listen; |
RAND_add(&Time,sizeof(Time),0); |
ERR_clear_error(); |
@@ -157,11 +160,15 @@ |
cb=s->info_callback; |
else if (s->ctx->info_callback != NULL) |
cb=s->ctx->info_callback; |
+ |
+ listen = s->d1->listen; |
/* init things to blank */ |
s->in_handshake++; |
if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s); |
+ s->d1->listen = listen; |
+ |
if (s->cert == NULL) |
{ |
SSLerr(SSL_F_DTLS1_ACCEPT,SSL_R_NO_CERTIFICATE_SET); |
@@ -271,11 +278,23 @@ |
s->init_num=0; |
+ /* Reflect ClientHello sequence to remain stateless while listening */ |
+ if (listen) |
+ { |
+ memcpy(s->s3->write_sequence, s->s3->read_sequence, sizeof(s->s3->write_sequence)); |
+ } |
+ |
/* If we're just listening, stop here */ |
- if (s->d1->listen && s->state == SSL3_ST_SW_SRVR_HELLO_A) |
+ if (listen && s->state == SSL3_ST_SW_SRVR_HELLO_A) |
{ |
ret = 2; |
s->d1->listen = 0; |
+ /* Set expected sequence numbers |
+ * to continue the handshake. |
+ */ |
+ s->d1->handshake_read_seq = 2; |
+ s->d1->handshake_write_seq = 1; |
+ s->d1->next_handshake_write_seq = 1; |
goto end; |
} |
@@ -284,14 +303,13 @@ |
case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A: |
case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B: |
- dtls1_start_timer(s); |
ret = dtls1_send_hello_verify_request(s); |
if ( ret <= 0) goto end; |
s->state=SSL3_ST_SW_FLUSH; |
s->s3->tmp.next_state=SSL3_ST_SR_CLNT_HELLO_A; |
- /* HelloVerifyRequests resets Finished MAC */ |
- if (s->client_version != DTLS1_BAD_VER) |
+ /* HelloVerifyRequest resets Finished MAC */ |
+ if (s->version != DTLS1_BAD_VER) |
ssl3_init_finished_mac(s); |
break; |
@@ -321,8 +339,9 @@ |
case SSL3_ST_SW_CERT_A: |
case SSL3_ST_SW_CERT_B: |
- /* Check if it is anon DH */ |
- if (!(s->s3->tmp.new_cipher->algorithms & SSL_aNULL)) |
+ /* Check if it is anon DH or normal PSK */ |
+ if (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) |
+ && !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)) |
{ |
dtls1_start_timer(s); |
ret=dtls1_send_server_certificate(s); |
@@ -350,13 +369,13 @@ |
case SSL3_ST_SW_KEY_EXCH_A: |
case SSL3_ST_SW_KEY_EXCH_B: |
- l=s->s3->tmp.new_cipher->algorithms; |
+ alg_k = s->s3->tmp.new_cipher->algorithm_mkey; |
/* clear this, it may get reset by |
* send_server_key_exchange */ |
if ((s->options & SSL_OP_EPHEMERAL_RSA) |
#ifndef OPENSSL_NO_KRB5 |
- && !(l & SSL_KRB5) |
+ && !(alg_k & SSL_kKRB5) |
#endif /* OPENSSL_NO_KRB5 */ |
) |
/* option SSL_OP_EPHEMERAL_RSA sends temporary RSA key |
@@ -367,11 +386,17 @@ |
else |
s->s3->tmp.use_rsa_tmp=0; |
- /* only send if a DH key exchange, fortezza or |
+ /* only send if a DH key exchange or |
* RSA but we have a sign only certificate */ |
if (s->s3->tmp.use_rsa_tmp |
- || (l & (SSL_DH|SSL_kFZA)) |
- || ((l & SSL_kRSA) |
+ /* PSK: send ServerKeyExchange if PSK identity |
+ * hint if provided */ |
+#ifndef OPENSSL_NO_PSK |
+ || ((alg_k & SSL_kPSK) && s->ctx->psk_identity_hint) |
+#endif |
+ || (alg_k & (SSL_kEDH|SSL_kDHr|SSL_kDHd)) |
+ || (alg_k & SSL_kEECDH) |
+ || ((alg_k & SSL_kRSA) |
&& (s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey == NULL |
|| (SSL_C_IS_EXPORT(s->s3->tmp.new_cipher) |
&& EVP_PKEY_size(s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey)*8 > SSL_C_EXPORT_PKEYLENGTH(s->s3->tmp.new_cipher) |
@@ -402,12 +427,15 @@ |
/* never request cert in anonymous ciphersuites |
* (see section "Certificate request" in SSL 3 drafts |
* and in RFC 2246): */ |
- ((s->s3->tmp.new_cipher->algorithms & SSL_aNULL) && |
+ ((s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) && |
/* ... except when the application insists on verification |
* (against the specs, but s3_clnt.c accepts this for SSL 3) */ |
!(s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) || |
- /* never request cert in Kerberos ciphersuites */ |
- (s->s3->tmp.new_cipher->algorithms & SSL_aKRB5)) |
+ /* never request cert in Kerberos ciphersuites */ |
+ (s->s3->tmp.new_cipher->algorithm_auth & SSL_aKRB5) |
+ /* With normal PSK Certificates and |
+ * Certificate Requests are omitted */ |
+ || (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)) |
{ |
/* no cert request */ |
skip=1; |
@@ -479,15 +507,30 @@ |
s->state=SSL3_ST_SR_CERT_VRFY_A; |
s->init_num=0; |
- /* We need to get hashes here so if there is |
- * a client cert, it can be verified */ |
- s->method->ssl3_enc->cert_verify_mac(s, |
- &(s->s3->finish_dgst1), |
- &(s->s3->tmp.cert_verify_md[0])); |
- s->method->ssl3_enc->cert_verify_mac(s, |
- &(s->s3->finish_dgst2), |
- &(s->s3->tmp.cert_verify_md[MD5_DIGEST_LENGTH])); |
+ if (ret == 2) |
+ { |
+ /* For the ECDH ciphersuites when |
+ * the client sends its ECDH pub key in |
+ * a certificate, the CertificateVerify |
+ * message is not sent. |
+ */ |
+ s->state=SSL3_ST_SR_FINISHED_A; |
+ s->init_num = 0; |
+ } |
+ else |
+ { |
+ s->state=SSL3_ST_SR_CERT_VRFY_A; |
+ s->init_num=0; |
+ /* We need to get hashes here so if there is |
+ * a client cert, it can be verified */ |
+ s->method->ssl3_enc->cert_verify_mac(s, |
+ NID_md5, |
+ &(s->s3->tmp.cert_verify_md[0])); |
+ s->method->ssl3_enc->cert_verify_mac(s, |
+ NID_sha1, |
+ &(s->s3->tmp.cert_verify_md[MD5_DIGEST_LENGTH])); |
+ } |
break; |
case SSL3_ST_SR_CERT_VRFY_A: |
@@ -686,12 +729,8 @@ |
buf = (unsigned char *)s->init_buf->data; |
msg = p = &(buf[DTLS1_HM_HEADER_LENGTH]); |
- if (s->client_version == DTLS1_BAD_VER) |
- *(p++) = DTLS1_BAD_VER>>8, |
- *(p++) = DTLS1_BAD_VER&0xff; |
- else |
- *(p++) = s->version >> 8, |
- *(p++) = s->version & 0xFF; |
+ *(p++) = s->version >> 8; |
+ *(p++) = s->version & 0xFF; |
if (s->ctx->app_gen_cookie_cb == NULL || |
s->ctx->app_gen_cookie_cb(s, s->d1->cookie, |
@@ -713,9 +752,6 @@ |
/* number of bytes to write */ |
s->init_num=p-buf; |
s->init_off=0; |
- |
- /* buffer the message to handle re-xmits */ |
- dtls1_buffer_message(s, 0); |
} |
/* s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B */ |
@@ -740,12 +776,8 @@ |
/* Do the message type and length last */ |
d=p= &(buf[DTLS1_HM_HEADER_LENGTH]); |
- if (s->client_version == DTLS1_BAD_VER) |
- *(p++)=DTLS1_BAD_VER>>8, |
- *(p++)=DTLS1_BAD_VER&0xff; |
- else |
- *(p++)=s->version>>8, |
- *(p++)=s->version&0xff; |
+ *(p++)=s->version>>8; |
+ *(p++)=s->version&0xff; |
/* Random stuff */ |
memcpy(p,s->s3->server_random,SSL3_RANDOM_SIZE); |
@@ -851,6 +883,13 @@ |
#ifndef OPENSSL_NO_DH |
DH *dh=NULL,*dhp; |
#endif |
+#ifndef OPENSSL_NO_ECDH |
+ EC_KEY *ecdh=NULL, *ecdhp; |
+ unsigned char *encodedPoint = NULL; |
+ int encodedlen = 0; |
+ int curve_id = 0; |
+ BN_CTX *bn_ctx = NULL; |
+#endif |
EVP_PKEY *pkey; |
unsigned char *p,*d; |
int al,i; |
@@ -865,7 +904,7 @@ |
EVP_MD_CTX_init(&md_ctx); |
if (s->state == SSL3_ST_SW_KEY_EXCH_A) |
{ |
- type=s->s3->tmp.new_cipher->algorithms & SSL_MKEY_MASK; |
+ type=s->s3->tmp.new_cipher->algorithm_mkey; |
cert=s->cert; |
buf=s->init_buf; |
@@ -959,7 +998,142 @@ |
} |
else |
#endif |
+#ifndef OPENSSL_NO_ECDH |
+ if (type & SSL_kEECDH) |
{ |
+ const EC_GROUP *group; |
+ |
+ ecdhp=cert->ecdh_tmp; |
+ if ((ecdhp == NULL) && (s->cert->ecdh_tmp_cb != NULL)) |
+ { |
+ ecdhp=s->cert->ecdh_tmp_cb(s, |
+ SSL_C_IS_EXPORT(s->s3->tmp.new_cipher), |
+ SSL_C_EXPORT_PKEYLENGTH(s->s3->tmp.new_cipher)); |
+ } |
+ if (ecdhp == NULL) |
+ { |
+ al=SSL_AD_HANDSHAKE_FAILURE; |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,SSL_R_MISSING_TMP_ECDH_KEY); |
+ goto f_err; |
+ } |
+ |
+ if (s->s3->tmp.ecdh != NULL) |
+ { |
+ EC_KEY_free(s->s3->tmp.ecdh); |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE, ERR_R_INTERNAL_ERROR); |
+ goto err; |
+ } |
+ |
+ /* Duplicate the ECDH structure. */ |
+ if (ecdhp == NULL) |
+ { |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,ERR_R_ECDH_LIB); |
+ goto err; |
+ } |
+ if ((ecdh = EC_KEY_dup(ecdhp)) == NULL) |
+ { |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,ERR_R_ECDH_LIB); |
+ goto err; |
+ } |
+ |
+ s->s3->tmp.ecdh=ecdh; |
+ if ((EC_KEY_get0_public_key(ecdh) == NULL) || |
+ (EC_KEY_get0_private_key(ecdh) == NULL) || |
+ (s->options & SSL_OP_SINGLE_ECDH_USE)) |
+ { |
+ if(!EC_KEY_generate_key(ecdh)) |
+ { |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,ERR_R_ECDH_LIB); |
+ goto err; |
+ } |
+ } |
+ |
+ if (((group = EC_KEY_get0_group(ecdh)) == NULL) || |
+ (EC_KEY_get0_public_key(ecdh) == NULL) || |
+ (EC_KEY_get0_private_key(ecdh) == NULL)) |
+ { |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,ERR_R_ECDH_LIB); |
+ goto err; |
+ } |
+ |
+ if (SSL_C_IS_EXPORT(s->s3->tmp.new_cipher) && |
+ (EC_GROUP_get_degree(group) > 163)) |
+ { |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,SSL_R_ECGROUP_TOO_LARGE_FOR_CIPHER); |
+ goto err; |
+ } |
+ |
+ /* XXX: For now, we only support ephemeral ECDH |
+ * keys over named (not generic) curves. For |
+ * supported named curves, curve_id is non-zero. |
+ */ |
+ if ((curve_id = |
+ tls1_ec_nid2curve_id(EC_GROUP_get_curve_name(group))) |
+ == 0) |
+ { |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,SSL_R_UNSUPPORTED_ELLIPTIC_CURVE); |
+ goto err; |
+ } |
+ |
+ /* Encode the public key. |
+ * First check the size of encoding and |
+ * allocate memory accordingly. |
+ */ |
+ encodedlen = EC_POINT_point2oct(group, |
+ EC_KEY_get0_public_key(ecdh), |
+ POINT_CONVERSION_UNCOMPRESSED, |
+ NULL, 0, NULL); |
+ |
+ encodedPoint = (unsigned char *) |
+ OPENSSL_malloc(encodedlen*sizeof(unsigned char)); |
+ bn_ctx = BN_CTX_new(); |
+ if ((encodedPoint == NULL) || (bn_ctx == NULL)) |
+ { |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,ERR_R_MALLOC_FAILURE); |
+ goto err; |
+ } |
+ |
+ |
+ encodedlen = EC_POINT_point2oct(group, |
+ EC_KEY_get0_public_key(ecdh), |
+ POINT_CONVERSION_UNCOMPRESSED, |
+ encodedPoint, encodedlen, bn_ctx); |
+ |
+ if (encodedlen == 0) |
+ { |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,ERR_R_ECDH_LIB); |
+ goto err; |
+ } |
+ |
+ BN_CTX_free(bn_ctx); bn_ctx=NULL; |
+ |
+ /* XXX: For now, we only support named (not |
+ * generic) curves in ECDH ephemeral key exchanges. |
+ * In this situation, we need four additional bytes |
+ * to encode the entire ServerECDHParams |
+ * structure. |
+ */ |
+ n = 4 + encodedlen; |
+ |
+ /* We'll generate the serverKeyExchange message |
+ * explicitly so we can set these to NULLs |
+ */ |
+ r[0]=NULL; |
+ r[1]=NULL; |
+ r[2]=NULL; |
+ r[3]=NULL; |
+ } |
+ else |
+#endif /* !OPENSSL_NO_ECDH */ |
+#ifndef OPENSSL_NO_PSK |
+ if (type & SSL_kPSK) |
+ { |
+ /* reserve size for record length and PSK identity hint*/ |
+ n+=2+strlen(s->ctx->psk_identity_hint); |
+ } |
+ else |
+#endif /* !OPENSSL_NO_PSK */ |
+ { |
al=SSL_AD_HANDSHAKE_FAILURE; |
SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE); |
goto f_err; |
@@ -970,7 +1144,8 @@ |
n+=2+nr[i]; |
} |
- if (!(s->s3->tmp.new_cipher->algorithms & SSL_aNULL)) |
+ if (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) |
+ && !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)) |
{ |
if ((pkey=ssl_get_sign_pkey(s,s->s3->tmp.new_cipher)) |
== NULL) |
@@ -1001,6 +1176,41 @@ |
p+=nr[i]; |
} |
+#ifndef OPENSSL_NO_ECDH |
+ if (type & SSL_kEECDH) |
+ { |
+ /* XXX: For now, we only support named (not generic) curves. |
+ * In this situation, the serverKeyExchange message has: |
+ * [1 byte CurveType], [2 byte CurveName] |
+ * [1 byte length of encoded point], followed by |
+ * the actual encoded point itself |
+ */ |
+ *p = NAMED_CURVE_TYPE; |
+ p += 1; |
+ *p = 0; |
+ p += 1; |
+ *p = curve_id; |
+ p += 1; |
+ *p = encodedlen; |
+ p += 1; |
+ memcpy((unsigned char*)p, |
+ (unsigned char *)encodedPoint, |
+ encodedlen); |
+ OPENSSL_free(encodedPoint); |
+ p += encodedlen; |
+ } |
+#endif |
+ |
+#ifndef OPENSSL_NO_PSK |
+ if (type & SSL_kPSK) |
+ { |
+ /* copy PSK identity hint */ |
+ s2n(strlen(s->ctx->psk_identity_hint), p); |
+ strncpy((char *)p, s->ctx->psk_identity_hint, strlen(s->ctx->psk_identity_hint)); |
+ p+=strlen(s->ctx->psk_identity_hint); |
+ } |
+#endif |
+ |
/* not anonymous */ |
if (pkey != NULL) |
{ |
@@ -1054,7 +1264,26 @@ |
} |
else |
#endif |
+#if !defined(OPENSSL_NO_ECDSA) |
+ if (pkey->type == EVP_PKEY_EC) |
{ |
+ /* let's do ECDSA */ |
+ EVP_SignInit_ex(&md_ctx,EVP_ecdsa(), NULL); |
+ EVP_SignUpdate(&md_ctx,&(s->s3->client_random[0]),SSL3_RANDOM_SIZE); |
+ EVP_SignUpdate(&md_ctx,&(s->s3->server_random[0]),SSL3_RANDOM_SIZE); |
+ EVP_SignUpdate(&md_ctx,&(d[DTLS1_HM_HEADER_LENGTH]),n); |
+ if (!EVP_SignFinal(&md_ctx,&(p[2]), |
+ (unsigned int *)&i,pkey)) |
+ { |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,ERR_LIB_ECDSA); |
+ goto err; |
+ } |
+ s2n(i,p); |
+ n+=i+2; |
+ } |
+ else |
+#endif |
+ { |
/* Is this error check actually needed? */ |
al=SSL_AD_HANDSHAKE_FAILURE; |
SSLerr(SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE,SSL_R_UNKNOWN_PKEY_TYPE); |
@@ -1080,6 +1309,10 @@ |
f_err: |
ssl3_send_alert(s,SSL3_AL_FATAL,al); |
err: |
+#ifndef OPENSSL_NO_ECDH |
+ if (encodedPoint != NULL) OPENSSL_free(encodedPoint); |
+ BN_CTX_free(bn_ctx); |
+#endif |
EVP_MD_CTX_cleanup(&md_ctx); |
return(-1); |
} |
@@ -1193,14 +1426,15 @@ |
if (s->state == SSL3_ST_SW_CERT_A) |
{ |
x=ssl_get_server_send_cert(s); |
- if (x == NULL && |
- /* VRS: allow null cert if auth == KRB5 */ |
- (s->s3->tmp.new_cipher->algorithms |
- & (SSL_MKEY_MASK|SSL_AUTH_MASK)) |
- != (SSL_aKRB5|SSL_kKRB5)) |
+ if (x == NULL) |
{ |
- SSLerr(SSL_F_DTLS1_SEND_SERVER_CERTIFICATE,ERR_R_INTERNAL_ERROR); |
- return(0); |
+ /* VRS: allow null cert if auth == KRB5 */ |
+ if ((s->s3->tmp.new_cipher->algorithm_mkey != SSL_kKRB5) || |
+ (s->s3->tmp.new_cipher->algorithm_auth != SSL_aKRB5)) |
+ { |
+ SSLerr(SSL_F_DTLS1_SEND_SERVER_CERTIFICATE,ERR_R_INTERNAL_ERROR); |
+ return(0); |
+ } |
} |
l=dtls1_output_cert_chain(s,x); |