summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDr. Stephen Henson <steve@openssl.org>2009-06-16 16:38:47 +0000
committerDr. Stephen Henson <steve@openssl.org>2009-06-16 16:38:47 +0000
commitf0288f05b92c3c206a515691f548b857f6aaa194 (patch)
tree2894cf72978684a519606752e712d00ccd0fd999
parent31db43df0859210a32af3708df08f0149c46ede0 (diff)
Submitted by: Artem Chuprina <ran@cryptocom.ru>
Reviewed by: steve@openssl.org Various GOST ciphersuite and ENGINE fixes. Including... Allow EVP_PKEY_set_derive_peerkey() in encryption operations. New flag when certificate verify should be omitted in client key exchange.
-rw-r--r--crypto/evp/pmeth_fn.c11
-rw-r--r--doc/crypto/EVP_PKEY_CTX_ctrl.pod2
-rw-r--r--engines/ccgost/e_gost_err.c4
-rw-r--r--engines/ccgost/e_gost_err.h2
-rw-r--r--engines/ccgost/gost2001_keyx.c51
-rw-r--r--engines/ccgost/gost94_keyx.c52
-rw-r--r--engines/ccgost/gost_asn1.c2
-rw-r--r--engines/ccgost/gost_lcl.h1
-rw-r--r--engines/ccgost/gost_pmeth.c9
-rw-r--r--ssl/s3_clnt.c43
-rw-r--r--ssl/s3_srvr.c64
-rw-r--r--ssl/ssl3.h1
12 files changed, 184 insertions, 58 deletions
diff --git a/crypto/evp/pmeth_fn.c b/crypto/evp/pmeth_fn.c
index edd1efa6df..c4676f2f8d 100644
--- a/crypto/evp/pmeth_fn.c
+++ b/crypto/evp/pmeth_fn.c
@@ -285,13 +285,13 @@ int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx)
int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer)
{
int ret;
- if (!ctx || !ctx->pmeth || !(ctx->pmeth->derive||ctx->pmeth->encrypt) || !ctx->pmeth->ctrl)
+ if (!ctx || !ctx->pmeth || !(ctx->pmeth->derive||ctx->pmeth->encrypt||ctx->pmeth->decrypt) || !ctx->pmeth->ctrl)
{
EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER,
EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
return -2;
}
- if (ctx->operation != EVP_PKEY_OP_DERIVE && ctx->operation != EVP_PKEY_OP_ENCRYPT)
+ if (ctx->operation != EVP_PKEY_OP_DERIVE && ctx->operation != EVP_PKEY_OP_ENCRYPT && ctx->operation != EVP_PKEY_OP_DECRYPT)
{
EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER,
EVP_R_OPERATON_NOT_INITIALIZED);
@@ -319,6 +319,11 @@ int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer)
return -1;
}
+ /* ran@cryptocom.ru: For clarity. The error is if parameters in peer are
+ * present (!missing) but don't match. EVP_PKEY_cmp_parameters may return
+ * 1 (match), 0 (don't match) and -2 (comparison is not defined). -1
+ * (different key types) is impossible here because it is checked earlier.
+ * -2 is OK for us here, as well as 1, so we can check for 0 only. */
if (!EVP_PKEY_missing_parameters(peer) &&
!EVP_PKEY_cmp_parameters(ctx->pkey, peer))
{
@@ -327,6 +332,8 @@ int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer)
return -1;
}
+ if (ctx->peerkey)
+ EVP_PKEY_free(ctx->peerkey);
ctx->peerkey = peer;
ret = ctx->pmeth->ctrl(ctx, EVP_PKEY_CTRL_PEER_KEY, 1, peer);
diff --git a/doc/crypto/EVP_PKEY_CTX_ctrl.pod b/doc/crypto/EVP_PKEY_CTX_ctrl.pod
index 6deccfbf95..37e2b72323 100644
--- a/doc/crypto/EVP_PKEY_CTX_ctrl.pod
+++ b/doc/crypto/EVP_PKEY_CTX_ctrl.pod
@@ -37,7 +37,7 @@ EVP_PKEY_ctrl, EVP_PKEY_ctrl_str - algorithm specific control operations
=head1 DESCRIPTION
The function EVP_PKEY_CTX_ctrl() sends a control operation to the context
-B<ctx>. The key type used must match B<keytype> if it is not zero. The parameter
+B<ctx>. The key type used must match B<keytype> if it is not -1. The parameter
B<optype> is a mask indicating which operations the control can be applied to.
The control command is indicated in B<cmd> and any additional arguments in
B<p1> and B<p2>.
diff --git a/engines/ccgost/e_gost_err.c b/engines/ccgost/e_gost_err.c
index 469027051a..9a79a374e2 100644
--- a/engines/ccgost/e_gost_err.c
+++ b/engines/ccgost/e_gost_err.c
@@ -1,6 +1,6 @@
/* e_gost_err.c */
/* ====================================================================
- * Copyright (c) 1999-2008 The OpenSSL Project. All rights reserved.
+ * Copyright (c) 1999-2009 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
@@ -120,10 +120,12 @@ static ERR_STRING_DATA GOST_str_reasons[]=
{ERR_REASON(GOST_R_BAD_KEY_PARAMETERS_FORMAT),"bad key parameters format"},
{ERR_REASON(GOST_R_BAD_PKEY_PARAMETERS_FORMAT),"bad pkey parameters format"},
{ERR_REASON(GOST_R_CANNOT_PACK_EPHEMERAL_KEY),"cannot pack ephemeral key"},
+{ERR_REASON(GOST_R_CTRL_CALL_FAILED) ,"ctrl call failed"},
{ERR_REASON(GOST_R_ERROR_COMPUTING_SHARED_KEY),"error computing shared key"},
{ERR_REASON(GOST_R_ERROR_PACKING_KEY_TRANSPORT_INFO),"error packing key transport info"},
{ERR_REASON(GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO),"error parsing key transport info"},
{ERR_REASON(GOST_R_INCOMPATIBLE_ALGORITHMS),"incompatible algorithms"},
+{ERR_REASON(GOST_R_INCOMPATIBLE_PEER_KEY),"incompatible peer key"},
{ERR_REASON(GOST_R_INVALID_CIPHER_PARAMS),"invalid cipher params"},
{ERR_REASON(GOST_R_INVALID_CIPHER_PARAM_OID),"invalid cipher param oid"},
{ERR_REASON(GOST_R_INVALID_DIGEST_TYPE) ,"invalid digest type"},
diff --git a/engines/ccgost/e_gost_err.h b/engines/ccgost/e_gost_err.h
index 652d1bb392..6dc500079d 100644
--- a/engines/ccgost/e_gost_err.h
+++ b/engines/ccgost/e_gost_err.h
@@ -118,10 +118,12 @@ void ERR_GOST_error(int function, int reason, char *file, int line);
#define GOST_R_BAD_KEY_PARAMETERS_FORMAT 99
#define GOST_R_BAD_PKEY_PARAMETERS_FORMAT 100
#define GOST_R_CANNOT_PACK_EPHEMERAL_KEY 101
+#define GOST_R_CTRL_CALL_FAILED 132
#define GOST_R_ERROR_COMPUTING_SHARED_KEY 102
#define GOST_R_ERROR_PACKING_KEY_TRANSPORT_INFO 103
#define GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO 104
#define GOST_R_INCOMPATIBLE_ALGORITHMS 105
+#define GOST_R_INCOMPATIBLE_PEER_KEY 131
#define GOST_R_INVALID_CIPHER_PARAMS 106
#define GOST_R_INVALID_CIPHER_PARAM_OID 107
#define GOST_R_INVALID_DIGEST_TYPE 108
diff --git a/engines/ccgost/gost2001_keyx.c b/engines/ccgost/gost2001_keyx.c
index 3b5a78ac3d..00759bcab0 100644
--- a/engines/ccgost/gost2001_keyx.c
+++ b/engines/ccgost/gost2001_keyx.c
@@ -203,6 +203,16 @@ int pkey_GOST01cp_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_le
ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
gkt->key_agreement_info->cipher = OBJ_nid2obj(param->nid);
if (key_is_ephemeral && sec_key) EVP_PKEY_free(sec_key);
+ if (!key_is_ephemeral)
+ {
+ /* Set control "public key from client certificate used" */
+ if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) <= 0)
+ {
+ GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT,
+ GOST_R_CTRL_CALL_FAILED);
+ goto err;
+ }
+ }
if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt,out?&out:NULL))>0) ret =1;
GOST_KEY_TRANSPORT_free(gkt);
return ret;
@@ -225,7 +235,7 @@ int pkey_GOST01cp_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, size_t * key_l
unsigned char sharedKey[32];
gost_ctx ctx;
const struct gost_cipher_info *param=NULL;
- EVP_PKEY *eph_key=NULL;
+ EVP_PKEY *eph_key=NULL, *peerkey=NULL;
if (!key)
{
@@ -239,18 +249,35 @@ int pkey_GOST01cp_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, size_t * key_l
GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
return -1;
}
-
+
+ /* If key transport structure contains public key, use it */
eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
- if (!eph_key) {
- eph_key = EVP_PKEY_CTX_get0_peerkey(pctx);
- if (! eph_key) {
+ if (eph_key)
+ {
+ if (EVP_PKEY_derive_set_peer(pctx, eph_key) <= 0)
+ {
GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
- GOST_R_NO_PEER_KEY);
+ GOST_R_INCOMPATIBLE_PEER_KEY);
goto err;
+ }
+ }
+ else
+ {
+ /* Set control "public key from client certificate used" */
+ if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) <= 0)
+ {
+ GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
+ GOST_R_CTRL_CALL_FAILED);
+ goto err;
+ }
+ }
+ peerkey = EVP_PKEY_CTX_get0_peerkey(pctx);
+ if (!peerkey)
+ {
+ GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT,
+ GOST_R_NO_PEER_KEY);
+ goto err;
}
- /* Increment reference count of peer key */
- CRYPTO_add(&(eph_key->references),1 ,CRYPTO_LOCK_EVP_PKEY);
- }
param = get_encryption_params(gkt->key_agreement_info->cipher);
gost_init(&ctx,param->sblock);
@@ -260,7 +287,7 @@ int pkey_GOST01cp_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, size_t * key_l
memcpy(wrappedKey+8,gkt->key_info->encrypted_key->data,32);
OPENSSL_assert(gkt->key_info->imit->length==4);
memcpy(wrappedKey+40,gkt->key_info->imit->data,4);
- VKO_compute_key(sharedKey,32,EC_KEY_get0_public_key(EVP_PKEY_get0(eph_key)),
+ VKO_compute_key(sharedKey,32,EC_KEY_get0_public_key(EVP_PKEY_get0(peerkey)),
EVP_PKEY_get0(priv),wrappedKey);
if (!keyUnwrapCryptoPro(&ctx,sharedKey,wrappedKey,key))
{
@@ -269,9 +296,9 @@ int pkey_GOST01cp_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, size_t * key_l
goto err;
}
- EVP_PKEY_free(eph_key);
- GOST_KEY_TRANSPORT_free(gkt);
ret=1;
err:
+ if (eph_key) EVP_PKEY_free(eph_key);
+ if (gkt) GOST_KEY_TRANSPORT_free(gkt);
return ret;
}
diff --git a/engines/ccgost/gost94_keyx.c b/engines/ccgost/gost94_keyx.c
index 5d04a17821..624be586a5 100644
--- a/engines/ccgost/gost94_keyx.c
+++ b/engines/ccgost/gost94_keyx.c
@@ -92,7 +92,6 @@ int pkey_GOST94cp_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
const struct gost_cipher_info *param=get_encryption_params(NULL);
EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(ctx);
struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx);
- int size=-1;
gost_ctx cctx;
int key_is_ephemeral=1;
EVP_PKEY *mykey = EVP_PKEY_CTX_get0_peerkey(ctx);
@@ -178,10 +177,20 @@ int pkey_GOST94cp_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
ASN1_OBJECT_free(gkt->key_agreement_info->cipher);
gkt->key_agreement_info->cipher = OBJ_nid2obj(param->nid);
*outlen = i2d_GOST_KEY_TRANSPORT(gkt,out?&out:NULL);
- if (!size)
+ if (*outlen <= 0)
{
GOSTerr(GOST_F_PKEY_GOST94CP_ENCRYPT,GOST_R_ERROR_PACKING_KEY_TRANSPORT_INFO);
- size=-1;
+ goto err;
+ }
+ if (!key_is_ephemeral)
+ {
+ /* Set control "public key from client certificate used" */
+ if (EVP_PKEY_CTX_ctrl(ctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) <= 0)
+ {
+ GOSTerr(GOST_F_PKEY_GOST94CP_ENCRYPT,
+ GOST_R_CTRL_CALL_FAILED);
+ goto err;
+ }
}
GOST_KEY_TRANSPORT_free(gkt);
return 1;
@@ -207,7 +216,7 @@ int pkey_GOST94cp_decrypt(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *key_len
unsigned char sharedKey[32];
gost_ctx cctx;
const struct gost_cipher_info *param=NULL;
- EVP_PKEY *eph_key=NULL;
+ EVP_PKEY *eph_key=NULL, *peerkey=NULL;
EVP_PKEY *priv= EVP_PKEY_CTX_get0_pkey(ctx);
if (!key)
@@ -224,19 +233,32 @@ int pkey_GOST94cp_decrypt(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *key_len
return 0;
}
eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key);
- /* No ephemeral key in the structure. Check peer key in the context
- */
- if (!eph_key) {
- eph_key = EVP_PKEY_CTX_get0_peerkey(ctx);
- if (! eph_key) {
+ if (eph_key)
+ {
+ if (EVP_PKEY_derive_set_peer(ctx, eph_key) <= 0)
+ {
GOSTerr(GOST_F_PKEY_GOST94CP_DECRYPT,
- GOST_R_NO_PEER_KEY);
+ GOST_R_INCOMPATIBLE_PEER_KEY);
goto err;
+ }
+ }
+ else
+ {
+ /* Set control "public key from client certificate used" */
+ if (EVP_PKEY_CTX_ctrl(ctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) <= 0)
+ {
+ GOSTerr(GOST_F_PKEY_GOST94CP_DECRYPT,
+ GOST_R_CTRL_CALL_FAILED);
+ goto err;
+ }
+ }
+ peerkey = EVP_PKEY_CTX_get0_peerkey(ctx);
+ if (!peerkey)
+ {
+ GOSTerr(GOST_F_PKEY_GOST94CP_DECRYPT,
+ GOST_R_NO_PEER_KEY);
+ goto err;
}
- /* Increment reference count of peer key */
- CRYPTO_add(&(eph_key->references),1 ,CRYPTO_LOCK_EVP_PKEY);
- }
-
param = get_encryption_params(gkt->key_agreement_info->cipher);
gost_init(&cctx,param->sblock);
@@ -246,7 +268,7 @@ int pkey_GOST94cp_decrypt(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *key_len
memcpy(wrappedKey+8,gkt->key_info->encrypted_key->data,32);
OPENSSL_assert(gkt->key_info->imit->length==4);
memcpy(wrappedKey+40,gkt->key_info->imit->data,4);
- make_cp_exchange_key(gost_get0_priv_key(priv),eph_key,sharedKey);
+ make_cp_exchange_key(gost_get0_priv_key(priv),peerkey,sharedKey);
if (!keyUnwrapCryptoPro(&cctx,sharedKey,wrappedKey,key))
{
GOSTerr(GOST_F_PKEY_GOST94CP_DECRYPT,
diff --git a/engines/ccgost/gost_asn1.c b/engines/ccgost/gost_asn1.c
index 76c18c1c66..318ecfce57 100644
--- a/engines/ccgost/gost_asn1.c
+++ b/engines/ccgost/gost_asn1.c
@@ -27,7 +27,7 @@ IMPLEMENT_ASN1_FUNCTIONS(GOST_KEY_INFO)
ASN1_NDEF_SEQUENCE(GOST_KEY_AGREEMENT_INFO) = {
ASN1_SIMPLE(GOST_KEY_AGREEMENT_INFO, cipher, ASN1_OBJECT),
- ASN1_IMP(GOST_KEY_AGREEMENT_INFO, ephem_key, X509_PUBKEY, 0),
+ ASN1_IMP_OPT(GOST_KEY_AGREEMENT_INFO, ephem_key, X509_PUBKEY, 0),
ASN1_SIMPLE(GOST_KEY_AGREEMENT_INFO, eph_iv, ASN1_OCTET_STRING)
} ASN1_NDEF_SEQUENCE_END(GOST_KEY_AGREEMENT_INFO)
diff --git a/engines/ccgost/gost_lcl.h b/engines/ccgost/gost_lcl.h
index 81fecd38a2..437a48cc86 100644
--- a/engines/ccgost/gost_lcl.h
+++ b/engines/ccgost/gost_lcl.h
@@ -47,6 +47,7 @@
int sign_param_nid; /* Should be set whenever parameters are filled */
EVP_MD *md;
unsigned char *shared_ukm;
+ int peer_key_used;
};
struct gost_mac_pmeth_data {
diff --git a/engines/ccgost/gost_pmeth.c b/engines/ccgost/gost_pmeth.c
index 2861d38327..caaea99d36 100644
--- a/engines/ccgost/gost_pmeth.c
+++ b/engines/ccgost/gost_pmeth.c
@@ -98,7 +98,14 @@ static int pkey_gost_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
pctx->shared_ukm=OPENSSL_malloc((int)p1);
memcpy(pctx->shared_ukm,p2,(int) p1);
return 1;
-
+ case EVP_PKEY_CTRL_PEER_KEY:
+ if (p1 == 0 || p1 == 1) /* call from EVP_PKEY_derive_set_peer */
+ return 1;
+ if (p1 == 2) /* TLS: peer key used? */
+ return pctx->peer_key_used;
+ if (p1 == 3) /* TLS: peer key used! */
+ return (pctx->peer_key_used = 1);
+ return -2;
}
return -2;
}
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index e0bfd0ceaf..861ce30138 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -404,6 +404,11 @@ int ssl3_connect(SSL *s)
s->state=SSL3_ST_CW_CHANGE_A;
s->s3->change_cipher_spec=0;
}
+ if (s->s3->flags & TLS1_FLAGS_SKIP_CERT_VERIFY)
+ {
+ s->state=SSL3_ST_CW_CHANGE_A;
+ s->s3->change_cipher_spec=0;
+ }
s->init_num=0;
break;
@@ -2416,7 +2421,7 @@ int ssl3_send_client_key_exchange(SSL *s)
size_t msglen;
unsigned int md_len;
int keytype;
- unsigned char premaster_secret[32],shared_ukm[32];
+ unsigned char premaster_secret[32],shared_ukm[32], tmp[256];
EVP_MD_CTX *ukm_hash;
EVP_PKEY *pub_key;
@@ -2442,16 +2447,13 @@ int ssl3_send_client_key_exchange(SSL *s)
/* Generate session key */
RAND_bytes(premaster_secret,32);
/* If we have client certificate, use its secret as peer key */
- if (s->cert->key->privatekey) {
- if (EVP_PKEY_derive_set_peer(pkey_ctx,s->cert->key->privatekey) <0) {
+ if (s->s3->tmp.cert_req && s->cert->key->privatekey) {
+ if (EVP_PKEY_derive_set_peer(pkey_ctx,s->cert->key->privatekey) <=0) {
/* If there was an error - just ignore it. Ephemeral key
* would be used
*/
ERR_clear_error();
- } else {
- /* Set flag "client cert key is used for key
- * exchange"*/
- }
+ }
}
/* Compute shared IV and store it in algorithm-specific
* context data */
@@ -2470,15 +2472,30 @@ int ssl3_send_client_key_exchange(SSL *s)
/* Make GOST keytransport blob message */
/*Encapsulate it into sequence */
*(p++)=V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED;
- *(p++)=0x81;
- msglen=256;
- if (EVP_PKEY_encrypt(pkey_ctx,(unsigned char *)p+1,&msglen,premaster_secret,32)<0) {
+ msglen=255;
+ if (EVP_PKEY_encrypt(pkey_ctx,tmp,&msglen,premaster_secret,32)<0) {
SSLerr(SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE,
SSL_R_LIBRARY_BUG);
goto err;
- }
- *(p++)= msglen & 0xff;
- n=msglen+3;
+ }
+ if (msglen >= 0x80)
+ {
+ *(p++)=0x81;
+ *(p++)= msglen & 0xff;
+ n=msglen+3;
+ }
+ else
+ {
+ *(p++)= msglen & 0xff;
+ n=msglen+2;
+ }
+ memcpy(p, tmp, msglen);
+ /* Check if pubkey from client certificate was used */
+ if (EVP_PKEY_CTX_ctrl(pkey_ctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 2, NULL) > 0)
+ {
+ /* Set flag "skip certificate verify" */
+ s->s3->flags |= TLS1_FLAGS_SKIP_CERT_VERIFY;
+ }
EVP_PKEY_CTX_free(pkey_ctx);
s->session->master_key_length=
s->method->ssl3_enc->generate_master_secret(s,
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 44065d7e89..66c063a7ba 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -514,6 +514,9 @@ int ssl3_accept(SSL *s)
* the client sends its ECDH pub key in
* a certificate, the CertificateVerify
* message is not sent.
+ * Also for GOST ciphersuites when
+ * the client uses its key from the certificate
+ * for key exchange.
*/
s->state=SSL3_ST_SR_FINISHED_A;
s->init_num = 0;
@@ -2496,33 +2499,70 @@ int ssl3_get_client_key_exchange(SSL *s)
else
#endif
if (alg_k & SSL_kGOST)
- {
+ {
+ int ret = 0;
EVP_PKEY_CTX *pkey_ctx;
- unsigned char premaster_secret[32];
- size_t outlen;
+ EVP_PKEY *client_pub_pkey = NULL;
+ unsigned char premaster_secret[32], *start;
+ size_t outlen, inlen;
- /* Get our certificate privatec key*/
+ /* Get our certificate private key*/
pkey_ctx = EVP_PKEY_CTX_new(s->cert->key->privatekey,NULL);
EVP_PKEY_decrypt_init(pkey_ctx);
+ /* If client certificate is present and is of the same type, maybe
+ * use it for key exchange. Don't mind errors from
+ * EVP_PKEY_derive_set_peer, because it is completely valid to use
+ * a client certificate for authorization only. */
+ client_pub_pkey = X509_get_pubkey(s->session->peer);
+ if (client_pub_pkey)
+ {
+ if (EVP_PKEY_derive_set_peer(pkey_ctx, client_pub_pkey) <= 0)
+ ERR_clear_error();
+ }
/* Decrypt session key */
- if ((*p!=( V_ASN1_SEQUENCE| V_ASN1_CONSTRUCTED)) || p[1]!=0x81 )
+ if ((*p!=( V_ASN1_SEQUENCE| V_ASN1_CONSTRUCTED)))
{
SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,SSL_R_DECRYPTION_FAILED);
- goto err;
- }
- if (EVP_PKEY_decrypt(pkey_ctx,premaster_secret,&outlen,p+3,p[2]) <0)
+ goto gerr;
+ }
+ if (p[1] == 0x81)
+ {
+ start = p+3;
+ inlen = p[2];
+ }
+ else if (p[1] < 0x80)
+ {
+ start = p+2;
+ inlen = p[1];
+ }
+ else
+ {
+ SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,SSL_R_DECRYPTION_FAILED);
+ goto gerr;
+ }
+ if (EVP_PKEY_decrypt(pkey_ctx,premaster_secret,&outlen,start,inlen) <=0)
{
SSLerr(SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE,SSL_R_DECRYPTION_FAILED);
- goto err;
+ goto gerr;
}
/* Generate master secret */
- EVP_PKEY_CTX_free(pkey_ctx);
s->session->master_key_length=
s->method->ssl3_enc->generate_master_secret(s,
s->session->master_key,premaster_secret,32);
-
- }
+ /* Check if pubkey from client certificate was used */
+ if (EVP_PKEY_CTX_ctrl(pkey_ctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 2, NULL) > 0)
+ ret = 2;
+ else
+ ret = 1;
+ gerr:
+ EVP_PKEY_free(client_pub_pkey);
+ EVP_PKEY_CTX_free(pkey_ctx);
+ if (ret)
+ return ret;
+ else
+ goto err;
+ }
else
{
al=SSL_AD_HANDSHAKE_FAILURE;
diff --git a/ssl/ssl3.h b/ssl/ssl3.h
index c2db3bd636..a4a6ce28c3 100644
--- a/ssl/ssl3.h
+++ b/ssl/ssl3.h
@@ -375,6 +375,7 @@ typedef struct ssl3_buffer_st
#define SSL3_FLAGS_DELAY_CLIENT_FINISHED 0x0002
#define SSL3_FLAGS_POP_BUFFER 0x0004
#define TLS1_FLAGS_TLS_PADDING_BUG 0x0008
+#define TLS1_FLAGS_SKIP_CERT_VERIFY 0x0010
typedef struct ssl3_state_st
{