* Hudson (tjh@cryptsoft.com).
*
*/
-
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * ECC cipher suite support in OpenSSL originally developed by
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
#ifdef REF_CHECK
# include <assert.h>
#endif
#include <stdio.h>
+#include "ssl_locl.h"
+#include "kssl_lcl.h"
#include <openssl/objects.h>
#include <openssl/lhash.h>
#include <openssl/x509v3.h>
-#include "ssl_locl.h"
-#include "kssl_lcl.h"
+#include "cryptlib.h"
const char *SSL_version_str=OPENSSL_VERSION_TEXT;
int SSL_clear(SSL *s)
{
- int state;
if (s->method == NULL)
{
return(0);
}
+ if (ssl_clear_bad_session(s))
+ {
+ SSL_SESSION_free(s->session);
+ s->session=NULL;
+ }
+
s->error=0;
s->hit=0;
s->shutdown=0;
}
#endif
- state=s->state; /* Keep to check if we throw away the session-id */
s->type=0;
s->state=SSL_ST_BEFORE|((s->server)?SSL_ST_ACCEPT:SSL_ST_CONNECT);
ssl_clear_cipher_ctx(s);
- if (ssl_clear_bad_session(s))
- {
- SSL_SESSION_free(s->session);
- s->session=NULL;
- }
-
s->first_packet=0;
#if 1
/* Check to see if we were changed into a different method, if
* so, revert back if we are not doing session-id reuse. */
- if ((s->session == NULL) && (s->method != s->ctx->method))
+ if (!s->in_handshake && (s->session == NULL) && (s->method != s->ctx->method))
{
s->method->ssl_free(s);
s->method=s->ctx->method;
s->verify_mode=ctx->verify_mode;
s->verify_depth=ctx->verify_depth;
s->sid_ctx_length=ctx->sid_ctx_length;
+ OPENSSL_assert(s->sid_ctx_length <= sizeof s->sid_ctx);
memcpy(&s->sid_ctx,&ctx->sid_ctx,sizeof(s->sid_ctx));
s->verify_callback=ctx->default_verify_callback;
s->generate_session_id=ctx->generate_session_id;
int SSL_CTX_set_session_id_context(SSL_CTX *ctx,const unsigned char *sid_ctx,
unsigned int sid_ctx_len)
{
- if(sid_ctx_len > SSL_MAX_SID_CTX_LENGTH)
+ if(sid_ctx_len > sizeof ctx->sid_ctx)
{
SSLerr(SSL_F_SSL_CTX_SET_SESSION_ID_CONTEXT,SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG);
return 0;
* any new session built out of this id/id_len and the ssl_version in
* use by this SSL. */
SSL_SESSION r, *p;
+
+ if(id_len > sizeof r.session_id)
+ return 0;
+
r.ssl_version = ssl->version;
r.session_id_length = id_len;
memcpy(r.session_id, id, id_len);
int SSL_renegotiate(SSL *s)
{
- s->new_session=1;
+ if (s->new_session == 0)
+ {
+ s->new_session=1;
+ }
return(s->method->ssl_renegotiate(s));
}
ret->key_arg=NULL;
ret->s2->conn_id=NULL; */
- ret->info_callback=0;
+ ret->info_callback=NULL;
ret->app_verify_callback=0;
ret->app_verify_arg=NULL;
abort(); /* ok */
}
#endif
- CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_CTX, a, &a->ex_data);
+ /*
+ * Free internal session cache. However: the remove_cb() may reference
+ * the ex_data of SSL_CTX, thus the ex_data store can only be removed
+ * after the sessions were flushed.
+ * As the ex_data handling routines might also touch the session cache,
+ * the most secure solution seems to be: empty (flush) the cache, then
+ * free ex_data, then finally free the cache.
+ * (See ticket [openssl.org #212].)
+ */
if (a->sessions != NULL)
- {
SSL_CTX_flush_sessions(a,0);
+
+ CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_CTX, a, &a->ex_data);
+
+ if (a->sessions != NULL)
lh_free(a->sessions);
- }
+
if (a->cert_store != NULL)
X509_STORE_free(a->cert_store);
if (a->cipher_list != NULL)
ctx->default_passwd_callback_userdata=u;
}
-void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx,int (*cb)(),char *arg)
+void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *,void *), void *arg)
{
- /* now
- * int (*cb)(X509_STORE_CTX *),
- * but should be
- * int (*cb)(X509_STORE_CTX *, void *arg)
- */
ctx->app_verify_callback=cb;
- ctx->app_verify_arg=arg; /* never used */
+ ctx->app_verify_arg=arg;
}
void SSL_CTX_set_verify(SSL_CTX *ctx,int mode,int (*cb)(int, X509_STORE_CTX *))
int rsa_enc_export,dh_rsa_export,dh_dsa_export;
int rsa_tmp_export,dh_tmp_export,kl;
unsigned long mask,emask;
+ int have_ecc_cert, have_ecdh_tmp, ecdh_ok, ecdsa_ok, ecc_pkey_size;
+ X509 *x = NULL;
+ EVP_PKEY *ecc_pkey = NULL;
+ int signature_nid = 0;
if (c == NULL) return;
dh_tmp=dh_tmp_export=0;
#endif
+#ifndef OPENSSL_NO_ECDH
+ have_ecdh_tmp=(c->ecdh_tmp != NULL || c->ecdh_tmp_cb != NULL);
+#endif
cpk= &(c->pkeys[SSL_PKEY_RSA_ENC]);
rsa_enc= (cpk->x509 != NULL && cpk->privatekey != NULL);
rsa_enc_export=(rsa_enc && EVP_PKEY_size(cpk->privatekey)*8 <= kl);
/* FIX THIS EAY EAY EAY */
dh_dsa= (cpk->x509 != NULL && cpk->privatekey != NULL);
dh_dsa_export=(dh_dsa && EVP_PKEY_size(cpk->privatekey)*8 <= kl);
-
+ cpk= &(c->pkeys[SSL_PKEY_ECC]);
+ have_ecc_cert= (cpk->x509 != NULL && cpk->privatekey != NULL);
mask=0;
emask=0;
if (rsa_enc || rsa_sign)
{
mask|=SSL_aRSA;
- emask|=SSL_aRSA;
+ mask|=SSL_aRSA;
}
if (dsa_sign)
emask|=SSL_kKRB5|SSL_aKRB5;
#endif
+ /* An ECC certificate may be usable for ECDH and/or
+ * ECDSA cipher suites depending on the key usage extension.
+ */
+ if (have_ecc_cert)
+ {
+ /* This call populates extension flags (ex_flags) */
+ x = (c->pkeys[SSL_PKEY_ECC]).x509;
+ X509_check_purpose(x, -1, 0);
+ ecdh_ok = (x->ex_flags & EXFLAG_KUSAGE) ?
+ (x->ex_kusage & X509v3_KU_KEY_AGREEMENT) : 1;
+ ecdsa_ok = (x->ex_flags & EXFLAG_KUSAGE) ?
+ (x->ex_kusage & X509v3_KU_DIGITAL_SIGNATURE) : 1;
+ ecc_pkey = X509_get_pubkey(x);
+ ecc_pkey_size = (ecc_pkey != NULL) ?
+ EVP_PKEY_bits(ecc_pkey) : 0;
+ EVP_PKEY_free(ecc_pkey);
+ if ((x->sig_alg) && (x->sig_alg->algorithm))
+ signature_nid = OBJ_obj2nid(x->sig_alg->algorithm);
+#ifndef OPENSSL_NO_ECDH
+ if (ecdh_ok)
+ {
+ if ((signature_nid == NID_md5WithRSAEncryption) ||
+ (signature_nid == NID_md4WithRSAEncryption) ||
+ (signature_nid == NID_md2WithRSAEncryption))
+ {
+ mask|=SSL_kECDH|SSL_aRSA;
+ if (ecc_pkey_size <= 163)
+ emask|=SSL_kECDH|SSL_aRSA;
+ }
+ if (signature_nid == NID_ecdsa_with_SHA1)
+ {
+ mask|=SSL_kECDH|SSL_aECDSA;
+ if (ecc_pkey_size <= 163)
+ emask|=SSL_kECDH|SSL_aECDSA;
+ }
+ }
+#endif
+#ifndef OPENSSL_NO_ECDSA
+ if (ecdsa_ok)
+ {
+ mask|=SSL_aECDSA;
+ emask|=SSL_aECDSA;
+ }
+#endif
+ }
+
+#ifndef OPENSSL_NO_ECDH
+ if (have_ecdh_tmp)
+ {
+ mask|=SSL_kECDHE;
+ emask|=SSL_kECDHE;
+ }
+#endif
c->mask=mask;
c->export_mask=emask;
c->valid=1;
}
+/* This handy macro borrowed from crypto/x509v3/v3_purp.c */
+#define ku_reject(x, usage) \
+ (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage)))
+
+int check_srvr_ecc_cert_and_alg(X509 *x, SSL_CIPHER *cs)
+ {
+ unsigned long alg = cs->algorithms;
+ EVP_PKEY *pkey = NULL;
+ int keysize = 0;
+ int signature_nid = 0;
+
+ if (SSL_C_IS_EXPORT(cs))
+ {
+ /* ECDH key length in export ciphers must be <= 163 bits */
+ pkey = X509_get_pubkey(x);
+ if (pkey == NULL) return 0;
+ keysize = EVP_PKEY_bits(pkey);
+ EVP_PKEY_free(pkey);
+ if (keysize > 163) return 0;
+ }
+
+ /* This call populates the ex_flags field correctly */
+ X509_check_purpose(x, -1, 0);
+ if ((x->sig_alg) && (x->sig_alg->algorithm))
+ signature_nid = OBJ_obj2nid(x->sig_alg->algorithm);
+ if (alg & SSL_kECDH)
+ {
+ /* key usage, if present, must allow key agreement */
+ if (ku_reject(x, X509v3_KU_KEY_AGREEMENT))
+ {
+ return 0;
+ }
+ if (alg & SSL_aECDSA)
+ {
+ /* signature alg must be ECDSA */
+ if (signature_nid != NID_ecdsa_with_SHA1)
+ {
+ return 0;
+ }
+ }
+ if (alg & SSL_aRSA)
+ {
+ /* signature alg must be RSA */
+ if ((signature_nid != NID_md5WithRSAEncryption) &&
+ (signature_nid != NID_md4WithRSAEncryption) &&
+ (signature_nid != NID_md2WithRSAEncryption))
+ {
+ return 0;
+ }
+ }
+ }
+ else if (alg & SSL_aECDSA)
+ {
+ /* key usage, if present, must allow signing */
+ if (ku_reject(x, X509v3_KU_DIGITAL_SIGNATURE))
+ {
+ return 0;
+ }
+ }
+
+ return 1; /* all checks are ok */
+ }
+
/* THIS NEEDS CLEANING UP */
X509 *ssl_get_server_send_cert(SSL *s)
{
mask=is_export?c->export_mask:c->mask;
kalg=alg&(SSL_MKEY_MASK|SSL_AUTH_MASK);
- if (kalg & SSL_kDHr)
+ if (kalg & SSL_kECDH)
+ {
+ /* we don't need to look at SSL_kECDHE
+ * since no certificate is needed for
+ * anon ECDH and for authenticated
+ * ECDHE, the check for the auth
+ * algorithm will set i correctly
+ * NOTE: For ECDH-RSA, we need an ECC
+ * not an RSA cert but for ECDHE-RSA
+ * we need an RSA cert. Placing the
+ * checks for SSL_kECDH before RSA
+ * checks ensures the correct cert is chosen.
+ */
+ i=SSL_PKEY_ECC;
+ }
+ else if (kalg & SSL_aECDSA)
+ {
+ i=SSL_PKEY_ECC;
+ }
+ else if (kalg & SSL_kDHr)
i=SSL_PKEY_DH_RSA;
else if (kalg & SSL_kDHd)
i=SSL_PKEY_DH_DSA;
return(NULL);
}
if (c->pkeys[i].x509 == NULL) return(NULL);
+
return(c->pkeys[i].x509);
}
else
return(NULL);
}
+ else if ((alg & SSL_aECDSA) &&
+ (c->pkeys[SSL_PKEY_ECC].privatekey != NULL))
+ return(c->pkeys[SSL_PKEY_ECC].privatekey);
else /* if (alg & SSL_aNULL) */
{
SSLerr(SSL_F_SSL_GET_SIGN_PKEY,ERR_R_INTERNAL_ERROR);
* and it would be rather hard to do anyway :-) */
if (s->session->session_id_length == 0) return;
- if ((s->ctx->session_cache_mode & mode)
- && (!s->hit)
- && SSL_CTX_add_session(s->ctx,s->session)
+ i=s->ctx->session_cache_mode;
+ if ((i & mode) && (!s->hit)
+ && ((i & SSL_SESS_CACHE_NO_INTERNAL_STORE)
+ || SSL_CTX_add_session(s->ctx,s->session))
&& (s->ctx->new_session_cb != NULL))
{
CRYPTO_add(&s->session->references,1,CRYPTO_LOCK_SSL_SESSION);
}
/* auto flush every 255 connections */
- i=s->ctx->session_cache_mode;
if ((!(i & SSL_SESS_CACHE_NO_AUTO_CLEAR)) &&
((i & mode) == mode))
{
}
#endif
-void SSL_set_info_callback(SSL *ssl,void (*cb)())
+void SSL_set_info_callback(SSL *ssl,
+ void (*cb)(const SSL *ssl,int type,int val))
{
ssl->info_callback=cb;
}
-void (*SSL_get_info_callback(SSL *ssl))(void)
+void (*SSL_get_info_callback(SSL *ssl))(const SSL *ssl,int type,int val)
{
- return((void (*)())ssl->info_callback);
+ return ssl->info_callback;
}
int SSL_state(SSL *ssl)
}
#endif
+#ifndef OPENSSL_NO_ECDH
+void SSL_CTX_set_tmp_ecdh_callback(SSL_CTX *ctx,EC_KEY *(*ecdh)(SSL *ssl,int is_export,
+ int keylength))
+ {
+ SSL_CTX_callback_ctrl(ctx,SSL_CTRL_SET_TMP_ECDH_CB,(void (*)())ecdh);
+ }
+
+void SSL_set_tmp_ecdh_callback(SSL *ssl,EC_KEY *(*ecdh)(SSL *ssl,int is_export,
+ int keylength))
+ {
+ SSL_callback_ctrl(ssl,SSL_CTRL_SET_TMP_ECDH_CB,(void (*)())ecdh);
+ }
+#endif
+
void SSL_CTX_set_msg_callback(SSL_CTX *ctx, void (*cb)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg))
{