diff options
author | Dmitry Belyavskiy <beldmit@gmail.com> | 2020-03-30 18:09:24 +0300 |
---|---|---|
committer | Dmitry Belyavskiy <beldmit@gmail.com> | 2020-05-19 13:02:43 +0300 |
commit | 5a5530a29abcf5d7ab7194d73b3807d568b06cbd (patch) | |
tree | 4084ebfee1f5e052d892e6b406c5b9358920170c /ssl/statem | |
parent | 0e139a02d59323e5d9c0ad87ea9c8c3914696b83 (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.c | 4 | ||||
-rw-r--r-- | ssl/statem/statem_clnt.c | 143 | ||||
-rw-r--r-- | ssl/statem/statem_local.h | 5 | ||||
-rw-r--r-- | ssl/statem/statem_srvr.c | 92 |
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, |