libressl/0078-ssl-add-support-for-GOST-KDF-key-exchange.patch

412 lines
12 KiB
Diff
Raw Permalink Normal View History

From e2409a33a5ffb21c41de9f213fecaf26e384d6a8 Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dbaryshkov@gmail.com>
Date: Wed, 1 Apr 2020 17:15:42 +0300
Subject: [PATCH 78/87] ssl: add support for GOST-KDF key exchange
Add support for GOST-KDF key exchange used by CTR-OMAC cipher suites.
Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
---
src/lib/libssl/s3_lib.c | 2 +
src/lib/libssl/ssl_ciph.c | 2 +-
src/lib/libssl/ssl_clnt.c | 87 +++++++++++++++++++---------------
src/lib/libssl/ssl_lib.c | 2 +-
src/lib/libssl/ssl_locl.h | 2 +
src/lib/libssl/ssl_srvr.c | 98 +++++++++++++++++++++++++--------------
src/lib/libssl/t1_lib.c | 42 +++++++++++++++++
7 files changed, 162 insertions(+), 73 deletions(-)
diff --git a/src/lib/libssl/s3_lib.c b/src/lib/libssl/s3_lib.c
index 18b9ad62f..106a2567a 100644
--- a/src/lib/libssl/s3_lib.c
+++ b/src/lib/libssl/s3_lib.c
@@ -2598,6 +2598,8 @@ ssl3_get_req_cert_types(SSL *s, CBB *cbb)
if ((alg_k & SSL_kGOST) != 0) {
if (!CBB_add_u8(cbb, TLS_CT_GOST01_SIGN))
return 0;
+ }
+ if (((alg_k & SSL_kGOST) != 0) || ((alg_k & SSL_kGOST_KDF) != 0)) {
if (!CBB_add_u8(cbb, TLS_CT_GOST12_256_SIGN))
return 0;
if (!CBB_add_u8(cbb, TLS_CT_GOST12_512_SIGN))
diff --git a/src/lib/libssl/ssl_ciph.c b/src/lib/libssl/ssl_ciph.c
index 9ef17e052..a3e0d396b 100644
--- a/src/lib/libssl/ssl_ciph.c
+++ b/src/lib/libssl/ssl_ciph.c
@@ -653,7 +653,7 @@ ssl_cipher_get_disabled(unsigned long *mkey, unsigned long *auth,
*/
if (EVP_PKEY_meth_find(NID_id_GostR3410_2001) == NULL) {
*auth |= SSL_aGOST01;
- *mkey |= SSL_kGOST;
+ *mkey |= SSL_kGOST | SSL_kGOST_KDF;
}
#ifdef SSL_FORBID_ENULL
diff --git a/src/lib/libssl/ssl_clnt.c b/src/lib/libssl/ssl_clnt.c
index 0a1b6ea24..731104253 100644
--- a/src/lib/libssl/ssl_clnt.c
+++ b/src/lib/libssl/ssl_clnt.c
@@ -2141,18 +2141,14 @@ ssl3_send_client_kex_ecdhe(SSL *s, SESS_CERT *sc, CBB *cbb)
}
static int
-ssl3_send_client_kex_gost(SSL *s, SESS_CERT *sess_cert, CBB *cbb)
+ssl3_send_client_kex_gost(SSL *s, SESS_CERT *sess_cert, CBB *cbb, unsigned int psexp)
{
- unsigned char premaster_secret[32], shared_ukm[32], tmp[256];
+ unsigned char premaster_secret[32], *tmp = NULL;
EVP_PKEY *pub_key = NULL;
EVP_PKEY_CTX *pkey_ctx;
X509 *peer_cert;
size_t msglen;
- unsigned int md_len;
- EVP_MD_CTX *ukm_hash;
int ret = -1;
- int nid;
- CBB gostblob;
/* Get server sertificate PKEY and create ctx from it */
peer_cert = sess_cert->peer_pkeys[SSL_PKEY_GOST01].x509;
@@ -2172,12 +2168,13 @@ ssl3_send_client_kex_gost(SSL *s, SESS_CERT *sess_cert, CBB *cbb)
EVP_PKEY_encrypt_init(pkey_ctx);
/* Generate session key. */
- arc4random_buf(premaster_secret, 32);
+ arc4random_buf(premaster_secret, sizeof(premaster_secret));
/*
* If we have client certificate, use its secret as peer key.
+ * Only for old (non-PSexp) key exchange.
*/
- if (S3I(s)->tmp.cert_req && s->cert->key->privatekey) {
+ if (!psexp && S3I(s)->tmp.cert_req && s->cert->key->privatekey) {
if (EVP_PKEY_derive_set_peer(pkey_ctx,
s->cert->key->privatekey) <=0) {
/*
@@ -2186,49 +2183,62 @@ ssl3_send_client_kex_gost(SSL *s, SESS_CERT *sess_cert, CBB *cbb)
*/
ERR_clear_error();
}
+ } else if (psexp) {
+ int format;
+
+ if (S3I(s)->hs.new_cipher->algorithm_enc == SSL_MAGMA_CTR_ACPKM)
+ format = GOST_ENC_FORMAT_PSKEY_MAGMA;
+ else
+ format = GOST_ENC_FORMAT_PSKEY_KUZNYECHIK;
+
+ if (EVP_PKEY_CTX_ctrl(pkey_ctx, -1, EVP_PKEY_OP_ENCRYPT,
+ EVP_PKEY_CTRL_GOST_ENC_FORMAT,
+ format, NULL) <= 0) {
+ SSLerror(s, ERR_R_EVP_LIB);
+ goto err;
+ }
+
}
/*
* Compute shared IV and store it in algorithm-specific context data.
*/
- ukm_hash = EVP_MD_CTX_new();
- if (ukm_hash == NULL) {
- SSLerror(s, ERR_R_MALLOC_FAILURE);
+ if (!tls1_set_gost_ukm(s, pkey_ctx, psexp))
goto err;
- }
-
- if (ssl_get_algorithm2(s) & SSL_HANDSHAKE_MAC_GOST94)
- nid = NID_id_GostR3411_94;
- else
- nid = NID_id_tc26_gost3411_2012_256;
- if (!EVP_DigestInit(ukm_hash, EVP_get_digestbynid(nid)))
- goto err;
- EVP_DigestUpdate(ukm_hash, s->s3->client_random, SSL3_RANDOM_SIZE);
- EVP_DigestUpdate(ukm_hash, s->s3->server_random, SSL3_RANDOM_SIZE);
- EVP_DigestFinal_ex(ukm_hash, shared_ukm, &md_len);
- EVP_MD_CTX_free(ukm_hash);
- if (EVP_PKEY_CTX_ctrl(pkey_ctx, -1, EVP_PKEY_OP_ENCRYPT,
- EVP_PKEY_CTRL_SET_IV, 8, shared_ukm) < 0) {
- SSLerror(s, SSL_R_LIBRARY_BUG);
- goto err;
- }
/*
* Make GOST keytransport blob message, encapsulate it into sequence.
*/
- msglen = 255;
- if (EVP_PKEY_encrypt(pkey_ctx, tmp, &msglen, premaster_secret,
- 32) < 0) {
+ if (EVP_PKEY_encrypt(pkey_ctx, NULL, &msglen,
+ premaster_secret, sizeof(premaster_secret)) < 0) {
SSLerror(s, SSL_R_LIBRARY_BUG);
goto err;
}
-
- if (!CBB_add_asn1(cbb, &gostblob, CBS_ASN1_SEQUENCE))
- goto err;
- if (!CBB_add_bytes(&gostblob, tmp, msglen))
+ if ((tmp = malloc(msglen)) == NULL) {
+ SSLerror(s, ERR_R_MALLOC_FAILURE);
goto err;
- if (!CBB_flush(cbb))
+ }
+ if (EVP_PKEY_encrypt(pkey_ctx, tmp, &msglen,
+ premaster_secret, sizeof(premaster_secret)) < 0) {
+ SSLerror(s, SSL_R_LIBRARY_BUG);
goto err;
+ }
+
+ if (psexp) {
+ if (!CBB_add_bytes(cbb, tmp, msglen))
+ goto err;
+ if (!CBB_flush(cbb))
+ goto err;
+ } else {
+ CBB gostblob;
+
+ if (!CBB_add_asn1(cbb, &gostblob, CBS_ASN1_SEQUENCE))
+ goto err;
+ if (!CBB_add_bytes(&gostblob, tmp, msglen))
+ goto err;
+ if (!CBB_flush(cbb))
+ goto err;
+ }
/* Check if pubkey from client certificate was used. */
if (EVP_PKEY_CTX_ctrl(pkey_ctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 2,
@@ -2283,7 +2293,10 @@ ssl3_send_client_key_exchange(SSL *s)
if (ssl3_send_client_kex_ecdhe(s, sess_cert, &kex) != 1)
goto err;
} else if (alg_k & SSL_kGOST) {
- if (ssl3_send_client_kex_gost(s, sess_cert, &kex) != 1)
+ if (ssl3_send_client_kex_gost(s, sess_cert, &kex, 0) != 1)
+ goto err;
+ } else if (alg_k & SSL_kGOST_KDF) {
+ if (ssl3_send_client_kex_gost(s, sess_cert, &kex, 1) != 1)
goto err;
} else {
ssl3_send_alert(s, SSL3_AL_FATAL,
diff --git a/src/lib/libssl/ssl_lib.c b/src/lib/libssl/ssl_lib.c
index 6ef2083f5..5495a74ad 100644
--- a/src/lib/libssl/ssl_lib.c
+++ b/src/lib/libssl/ssl_lib.c
@@ -2000,7 +2000,7 @@ ssl_set_cert_masks(CERT *c, const SSL_CIPHER *cipher)
cpk = &(c->pkeys[SSL_PKEY_GOST01]);
if (cpk->x509 != NULL && cpk->privatekey != NULL) {
- mask_k |= SSL_kGOST;
+ mask_k |= SSL_kGOST | SSL_kGOST_KDF;
mask_a |= SSL_aGOST01;
}
diff --git a/src/lib/libssl/ssl_locl.h b/src/lib/libssl/ssl_locl.h
index 72646fa8c..b07be5ab2 100644
--- a/src/lib/libssl/ssl_locl.h
+++ b/src/lib/libssl/ssl_locl.h
@@ -1373,6 +1373,8 @@ int ssl_check_serverhello_tlsext(SSL *s);
int tls1_process_ticket(SSL *s, CBS *session_id, CBS *ext_block,
SSL_SESSION **ret);
+int tls1_set_gost_ukm(SSL *s, EVP_PKEY_CTX *pkey_ctx, unsigned int psexp);
+
long ssl_get_algorithm2(SSL *s);
int tls1_check_ec_server_key(SSL *s);
diff --git a/src/lib/libssl/ssl_srvr.c b/src/lib/libssl/ssl_srvr.c
index 69e547cbe..1d924617c 100644
--- a/src/lib/libssl/ssl_srvr.c
+++ b/src/lib/libssl/ssl_srvr.c
@@ -1935,10 +1935,10 @@ ssl3_get_client_kex_ecdhe(SSL *s, CBS *cbs)
}
static int
-ssl3_get_client_kex_gost(SSL *s, CBS *cbs)
+ssl3_get_client_kex_gost(SSL *s, CBS *cbs, int psexp)
{
EVP_PKEY_CTX *pkey_ctx;
- EVP_PKEY *client_pub_pkey = NULL, *pk = NULL;
+ EVP_PKEY *pk = NULL;
unsigned char premaster_secret[32];
unsigned long alg_a;
size_t outlen = 32;
@@ -1954,56 +1954,83 @@ ssl3_get_client_kex_gost(SSL *s, CBS *cbs)
if ((pkey_ctx = EVP_PKEY_CTX_new(pk, NULL)) == NULL)
goto err;
if (EVP_PKEY_decrypt_init(pkey_ctx) <= 0)
- goto gerr;
+ goto err;
- /*
- * 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.
- */
- if ((client_pub_pkey = X509_get_pubkey(s->session->peer)) != NULL) {
- if (EVP_PKEY_derive_set_peer(pkey_ctx,
- client_pub_pkey) <= 0)
- ERR_clear_error();
+ if (!psexp) {
+ EVP_PKEY *client_pub_pkey = X509_get_pubkey(s->session->peer);
+ /*
+ * 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.
+ */
+ if (client_pub_pkey != NULL) {
+ if (EVP_PKEY_derive_set_peer(pkey_ctx, client_pub_pkey) <= 0)
+ ERR_clear_error();
+ EVP_PKEY_free(client_pub_pkey);
+ }
+
+ /*
+ * Compute shared IV and store it in algorithm-specific context data.
+ */
+ if (!tls1_set_gost_ukm(s, pkey_ctx, psexp))
+ goto err;
+
+ /* Decrypt session key */
+ if (!CBS_get_asn1(cbs, &gostblob, CBS_ASN1_SEQUENCE))
+ goto truncated;
+ } else {
+ int format;
+
+ if (S3I(s)->hs.new_cipher->algorithm_enc == SSL_MAGMA_CTR_ACPKM)
+ format = GOST_ENC_FORMAT_PSKEY_MAGMA;
+ else
+ format = GOST_ENC_FORMAT_PSKEY_KUZNYECHIK;
+
+ if (EVP_PKEY_CTX_ctrl(pkey_ctx, -1, EVP_PKEY_OP_DECRYPT,
+ EVP_PKEY_CTRL_GOST_ENC_FORMAT,
+ format, NULL) <= 0) {
+ SSLerror(s, ERR_R_EVP_LIB);
+ goto err;
+ }
+
+ /*
+ * Compute shared IV and store it in algorithm-specific context data.
+ */
+ if (!tls1_set_gost_ukm(s, pkey_ctx, psexp))
+ goto err;
+
+ /* Decrypt session key */
+ if (!CBS_get_asn1_element(cbs, &gostblob, CBS_ASN1_SEQUENCE))
+ goto truncated;
}
- /* Decrypt session key */
- if (!CBS_get_asn1(cbs, &gostblob, CBS_ASN1_SEQUENCE))
- goto truncated;
if (CBS_len(cbs) != 0)
goto truncated;
if (EVP_PKEY_decrypt(pkey_ctx, premaster_secret, &outlen,
- CBS_data(&gostblob), CBS_len(&gostblob)) <= 0) {
+ CBS_data(&gostblob), CBS_len(&gostblob)) <= 0 ||
+ outlen != 32) {
SSLerror(s, SSL_R_DECRYPTION_FAILED);
- goto gerr;
+ goto err;
}
/* Generate master secret */
s->session->master_key_length =
tls1_generate_master_secret(
- s, s->session->master_key, premaster_secret, 32);
+ s, s->session->master_key, premaster_secret, outlen);
- /* 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);
+ ret = 1;
+
+err:
EVP_PKEY_CTX_free(pkey_ctx);
- if (ret)
- return (ret);
- else
- goto err;
+
+ return ret;
truncated:
al = SSL_AD_DECODE_ERROR;
SSLerror(s, SSL_R_BAD_PACKET_LENGTH);
ssl3_send_alert(s, SSL3_AL_FATAL, al);
- err:
return (-1);
}
@@ -2038,7 +2065,10 @@ ssl3_get_client_key_exchange(SSL *s)
if (ssl3_get_client_kex_ecdhe(s, &cbs) != 1)
goto err;
} else if (alg_k & SSL_kGOST) {
- if (ssl3_get_client_kex_gost(s, &cbs) != 1)
+ if (ssl3_get_client_kex_gost(s, &cbs, 0) != 1)
+ goto err;
+ } else if (alg_k & SSL_kGOST_KDF) {
+ if (ssl3_get_client_kex_gost(s, &cbs, 1) != 1)
goto err;
} else {
al = SSL_AD_HANDSHAKE_FAILURE;
diff --git a/src/lib/libssl/t1_lib.c b/src/lib/libssl/t1_lib.c
index 580ae4b19..3da2ebb8c 100644
--- a/src/lib/libssl/t1_lib.c
+++ b/src/lib/libssl/t1_lib.c
@@ -1029,3 +1029,45 @@ tls_decrypt_ticket(SSL *s, CBS *session_id, CBS *ticket, SSL_SESSION **psess)
return ret;
}
+
+/*
+ * Compute shared IV and store it in algorithm-specific context data.
+ */
+int
+tls1_set_gost_ukm(SSL *s, EVP_PKEY_CTX *pkey_ctx, unsigned int psexp)
+{
+ unsigned char shared_ukm[32];
+ unsigned int md_len;
+ EVP_MD_CTX *ukm_hash;
+ int nid;
+
+ ukm_hash = EVP_MD_CTX_new();
+ if (ukm_hash == NULL) {
+ SSLerror(s, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ if (ssl_get_algorithm2(s) & SSL_HANDSHAKE_MAC_GOST94)
+ nid = NID_id_GostR3411_94;
+ else
+ nid = NID_id_tc26_gost3411_2012_256;
+ if (!EVP_DigestInit(ukm_hash, EVP_get_digestbynid(nid)) ||
+ !EVP_DigestUpdate(ukm_hash, s->s3->client_random, SSL3_RANDOM_SIZE) ||
+ !EVP_DigestUpdate(ukm_hash, s->s3->server_random, SSL3_RANDOM_SIZE) ||
+ !EVP_DigestFinal_ex(ukm_hash, shared_ukm, &md_len)) {
+ SSLerror(s, ERR_R_EVP_LIB);
+ goto err;
+ }
+
+ if (EVP_PKEY_CTX_ctrl(pkey_ctx, -1, -1,
+ EVP_PKEY_CTRL_SET_IV, psexp ? md_len : 8, shared_ukm) < 0) {
+ SSLerror(s, SSL_R_LIBRARY_BUG);
+ goto err;
+ }
+ EVP_MD_CTX_free(ukm_hash);
+ return 1;
+
+err:
+ EVP_MD_CTX_free(ukm_hash);
+ return 0;
+}
--
2.17.1