summaryrefslogtreecommitdiffstats
path: root/ssl/statem
diff options
context:
space:
mode:
authorDmitry Belyavskiy <beldmit@gmail.com>2020-03-30 18:09:24 +0300
committerDmitry Belyavskiy <beldmit@gmail.com>2020-05-19 13:02:43 +0300
commit5a5530a29abcf5d7ab7194d73b3807d568b06cbd (patch)
tree4084ebfee1f5e052d892e6b406c5b9358920170c /ssl/statem
parent0e139a02d59323e5d9c0ad87ea9c8c3914696b83 (diff)
New Russian TLS 1.2 implementation
Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/11442)
Diffstat (limited to 'ssl/statem')
-rw-r--r--ssl/statem/extensions_srvr.c4
-rw-r--r--ssl/statem/statem_clnt.c143
-rw-r--r--ssl/statem/statem_local.h5
-rw-r--r--ssl/statem/statem_srvr.c92
4 files changed, 243 insertions, 1 deletions
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index e33b671a05..aa71cec7e9 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -1648,7 +1648,9 @@ EXT_RETURN tls_construct_stoc_etm(SSL *s, WPACKET *pkt, unsigned int context,
if (s->s3.tmp.new_cipher->algorithm_mac == SSL_AEAD
|| s->s3.tmp.new_cipher->algorithm_enc == SSL_RC4
|| s->s3.tmp.new_cipher->algorithm_enc == SSL_eGOST2814789CNT
- || s->s3.tmp.new_cipher->algorithm_enc == SSL_eGOST2814789CNT12) {
+ || s->s3.tmp.new_cipher->algorithm_enc == SSL_eGOST2814789CNT12
+ || s->s3.tmp.new_cipher->algorithm_enc == SSL_MAGMA
+ || s->s3.tmp.new_cipher->algorithm_enc == SSL_KUZNYECHIK) {
s->ext.use_etm = 0;
return EXT_RETURN_NOT_SENT;
}
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index 4e43117ca2..67d8ae8ce6 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -3314,6 +3314,146 @@ static int tls_construct_cke_gost(SSL *s, WPACKET *pkt)
#endif
}
+#ifndef OPENSSL_NO_GOST
+int gost18_cke_cipher_nid(const SSL *s)
+{
+ if ((s->s3.tmp.new_cipher->algorithm_enc & SSL_MAGMA) != 0)
+ return NID_magma_ctr;
+ else if ((s->s3.tmp.new_cipher->algorithm_enc & SSL_KUZNYECHIK) != 0)
+ return NID_kuznyechik_ctr;
+
+ return NID_undef;
+}
+
+int gost_ukm(const SSL *s, unsigned char *dgst_buf)
+{
+ EVP_MD_CTX * hash = NULL;
+ unsigned int md_len;
+ const EVP_MD *md = ssl_evp_md_fetch(s->ctx->libctx, NID_id_GostR3411_2012_256, s->ctx->propq);
+
+ if (md == NULL)
+ return 0;
+
+ if ((hash = EVP_MD_CTX_new()) == NULL
+ || EVP_DigestInit(hash, md) <= 0
+ || EVP_DigestUpdate(hash, s->s3.client_random, SSL3_RANDOM_SIZE) <= 0
+ || EVP_DigestUpdate(hash, s->s3.server_random, SSL3_RANDOM_SIZE) <= 0
+ || EVP_DigestFinal_ex(hash, dgst_buf, &md_len) <= 0) {
+ EVP_MD_CTX_free(hash);
+ ssl_evp_md_free(md);
+ return 0;
+ }
+
+ EVP_MD_CTX_free(hash);
+ ssl_evp_md_free(md);
+ return 1;
+}
+#endif
+
+static int tls_construct_cke_gost18(SSL *s, WPACKET *pkt)
+{
+#ifndef OPENSSL_NO_GOST
+ /* GOST 2018 key exchange message creation */
+ unsigned char rnd_dgst[32], tmp[255];
+ EVP_PKEY_CTX *pkey_ctx = NULL;
+ X509 *peer_cert;
+ unsigned char *pms = NULL;
+ size_t pmslen = 0;
+ size_t msglen;
+ int cipher_nid = gost18_cke_cipher_nid(s);
+
+ if (cipher_nid == NID_undef) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ if (gost_ukm(s, rnd_dgst) <= 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ /* Pre-master secret - random bytes */
+ pmslen = 32;
+ pms = OPENSSL_malloc(pmslen);
+ if (pms == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ if (RAND_bytes_ex(s->ctx->libctx, pms, (int)pmslen) <= 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ /* Get server certificate PKEY and create ctx from it */
+ peer_cert = s->session->peer;
+ if (peer_cert == NULL) {
+ SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER);
+ return 0;
+ }
+
+ pkey_ctx = EVP_PKEY_CTX_new_from_pkey(s->ctx->libctx, X509_get0_pubkey(peer_cert), s->ctx->propq);
+ if (pkey_ctx == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ if (EVP_PKEY_encrypt_init(pkey_ctx) <= 0 ) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ };
+
+ /* Reuse EVP_PKEY_CTRL_SET_IV, make choice in engine code */
+ if (EVP_PKEY_CTX_ctrl(pkey_ctx, -1, EVP_PKEY_OP_ENCRYPT,
+ EVP_PKEY_CTRL_SET_IV, 32, rnd_dgst) < 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ SSL_R_LIBRARY_BUG);
+ goto err;
+ }
+
+ if (EVP_PKEY_CTX_ctrl(pkey_ctx, -1, EVP_PKEY_OP_ENCRYPT,
+ EVP_PKEY_CTRL_CIPHER, cipher_nid, NULL) < 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ SSL_R_LIBRARY_BUG);
+ goto err;
+ }
+
+ msglen = 255;
+ if (EVP_PKEY_encrypt(pkey_ctx, tmp, &msglen, pms, pmslen) <= 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ SSL_R_LIBRARY_BUG);
+ goto err;
+ }
+
+ if (!WPACKET_memcpy(pkt, tmp, msglen)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ EVP_PKEY_CTX_free(pkey_ctx);
+ s->s3.tmp.pms = pms;
+ s->s3.tmp.pmslen = pmslen;
+
+ return 1;
+ err:
+ EVP_PKEY_CTX_free(pkey_ctx);
+ OPENSSL_clear_free(pms, pmslen);
+ return 0;
+#else
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+#endif
+}
+
static int tls_construct_cke_srp(SSL *s, WPACKET *pkt)
{
#ifndef OPENSSL_NO_SRP
@@ -3370,6 +3510,9 @@ int tls_construct_client_key_exchange(SSL *s, WPACKET *pkt)
} else if (alg_k & SSL_kGOST) {
if (!tls_construct_cke_gost(s, pkt))
goto err;
+ } else if (alg_k & SSL_kGOST18) {
+ if (!tls_construct_cke_gost18(s, pkt))
+ goto err;
} else if (alg_k & SSL_kSRP) {
if (!tls_construct_cke_srp(s, pkt))
goto err;
diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h
index f4242fa2a4..6a4708cee9 100644
--- a/ssl/statem/statem_local.h
+++ b/ssl/statem/statem_local.h
@@ -153,6 +153,11 @@ __owur MSG_PROCESS_RETURN tls_process_next_proto(SSL *s, PACKET *pkt);
__owur int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt);
MSG_PROCESS_RETURN tls_process_end_of_early_data(SSL *s, PACKET *pkt);
+#ifndef OPENSSL_NO_GOST
+/* These functions are used in GOST18 CKE, both for client and server */
+int gost18_cke_cipher_nid(const SSL *s);
+int gost_ukm(const SSL *s, unsigned char *dgst_buf);
+#endif
/* Extension processing */
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index d1d86ea5e6..e5340b4e7f 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -3430,6 +3430,93 @@ static int tls_process_cke_gost(SSL *s, PACKET *pkt)
#endif
}
+static int tls_process_cke_gost18(SSL *s, PACKET *pkt)
+{
+#ifndef OPENSSL_NO_GOST
+ unsigned char rnd_dgst[32];
+ EVP_PKEY_CTX *pkey_ctx = NULL;
+ EVP_PKEY *pk = NULL;
+ unsigned char premaster_secret[32];
+ const unsigned char *start = NULL;
+ size_t outlen = 32, inlen = 0;
+ int ret = 0;
+ int cipher_nid = gost18_cke_cipher_nid(s);
+
+ if (cipher_nid == NID_undef) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ if (gost_ukm(s, rnd_dgst) <= 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CKE_GOST18,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ /* Get our certificate private key */
+ pk = s->cert->pkeys[SSL_PKEY_GOST12_512].privatekey != NULL ?
+ s->cert->pkeys[SSL_PKEY_GOST12_512].privatekey :
+ s->cert->pkeys[SSL_PKEY_GOST12_256].privatekey;
+ if (pk == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_GOST18,
+ SSL_R_BAD_HANDSHAKE_STATE);
+ goto err;
+ }
+
+ pkey_ctx = EVP_PKEY_CTX_new_from_pkey(s->ctx->libctx, pk, s->ctx->propq);
+ if (pkey_ctx == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_GOST18,
+ ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ if (EVP_PKEY_decrypt_init(pkey_ctx) <= 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_GOST18,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ /* Reuse EVP_PKEY_CTRL_SET_IV, make choice in engine code depending on size */
+ if (EVP_PKEY_CTX_ctrl(pkey_ctx, -1, EVP_PKEY_OP_DECRYPT,
+ EVP_PKEY_CTRL_SET_IV, 32, rnd_dgst) < 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_GOST18,
+ SSL_R_LIBRARY_BUG);
+ goto err;
+ }
+
+ if (EVP_PKEY_CTX_ctrl(pkey_ctx, -1, EVP_PKEY_OP_DECRYPT,
+ EVP_PKEY_CTRL_CIPHER, cipher_nid, NULL) < 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_GOST18,
+ SSL_R_LIBRARY_BUG);
+ goto err;
+ }
+ inlen = PACKET_remaining(pkt);
+ start = PACKET_data(pkt);
+
+ if (EVP_PKEY_decrypt(pkey_ctx, premaster_secret, &outlen, start, inlen) <= 0) {
+ SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_CKE_GOST18,
+ SSL_R_DECRYPTION_FAILED);
+ goto err;
+ }
+ /* Generate master secret */
+ if (!ssl_generate_master_secret(s, premaster_secret,
+ sizeof(premaster_secret), 0)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+ ret = 1;
+
+ err:
+ EVP_PKEY_CTX_free(pkey_ctx);
+ return ret;
+#else
+ /* Should never happen */
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_GOST18,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+#endif
+}
+
MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL *s, PACKET *pkt)
{
unsigned long alg_k;
@@ -3480,6 +3567,11 @@ MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL *s, PACKET *pkt)
/* SSLfatal() already called */
goto err;
}
+ } else if (alg_k & SSL_kGOST18) {
+ if (!tls_process_cke_gost18(s, pkt)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
} else {
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
SSL_F_TLS_PROCESS_CLIENT_KEY_EXCHANGE,