From 5a5530a29abcf5d7ab7194d73b3807d568b06cbd Mon Sep 17 00:00:00 2001 From: Dmitry Belyavskiy Date: Mon, 30 Mar 2020 18:09:24 +0300 Subject: [PATCH] New Russian TLS 1.2 implementation Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/11442) --- ssl/record/ssl3_record.c | 34 ++++++++- ssl/s3_lib.c | 48 ++++++++++-- ssl/ssl_ciph.c | 51 ++++++++++--- ssl/ssl_err.c | 4 +- ssl/ssl_lib.c | 4 +- ssl/ssl_local.h | 19 ++++- ssl/statem/extensions_srvr.c | 4 +- ssl/statem/statem_clnt.c | 143 +++++++++++++++++++++++++++++++++++ ssl/statem/statem_local.h | 5 ++ ssl/statem/statem_srvr.c | 92 ++++++++++++++++++++++ ssl/t1_enc.c | 18 ++++- ssl/t1_lib.c | 17 ++++- ssl/t1_trce.c | 17 ++++- 13 files changed, 428 insertions(+), 28 deletions(-) diff --git a/ssl/record/ssl3_record.c b/ssl/record/ssl3_record.c index 24b42098db..a2f7f848d1 100644 --- a/ssl/record/ssl3_record.c +++ b/ssl/record/ssl3_record.c @@ -977,6 +977,8 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending) unsigned char padval; int imac_size; const EVP_CIPHER *enc; + int tlstree_enc = sending ? (s->mac_flags & SSL_MAC_FLAG_WRITE_MAC_TLSTREE) + : (s->mac_flags & SSL_MAC_FLAG_READ_MAC_TLSTREE); if (n_recs == 0) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC, @@ -1156,6 +1158,27 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending) } } + if (!SSL_IS_DTLS(s) && tlstree_enc) { + unsigned char *seq; + int decrement_seq = 0; + + /* + * When sending, seq is incremented after MAC calculation. + * So if we are in ETM mode, we use seq 'as is' in the ctrl-function. + * Otherwise we have to decrease it in the implementation + */ + if (sending && !SSL_WRITE_ETM(s)) + decrement_seq = 1; + + seq = sending ? RECORD_LAYER_get_write_sequence(&s->rlayer) + : RECORD_LAYER_get_read_sequence(&s->rlayer); + if (EVP_CIPHER_CTX_ctrl(ds, EVP_CTRL_TLSTREE, decrement_seq, seq) <= 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC, + ERR_R_INTERNAL_ERROR); + return -1; + } + } + /* TODO(size_t): Convert this call */ tmpr = EVP_Cipher(ds, recs[0].data, recs[0].input, (unsigned int)reclen[0]); @@ -1319,8 +1342,10 @@ int tls1_mac(SSL *ssl, SSL3_RECORD *rec, unsigned char *md, int sending) int i; EVP_MD_CTX *hmac = NULL, *mac_ctx; unsigned char header[13]; - int stream_mac = (sending ? (ssl->mac_flags & SSL_MAC_FLAG_WRITE_MAC_STREAM) - : (ssl->mac_flags & SSL_MAC_FLAG_READ_MAC_STREAM)); + int stream_mac = sending ? (ssl->mac_flags & SSL_MAC_FLAG_WRITE_MAC_STREAM) + : (ssl->mac_flags & SSL_MAC_FLAG_READ_MAC_STREAM); + int tlstree_mac = sending ? (ssl->mac_flags & SSL_MAC_FLAG_WRITE_MAC_TLSTREE) + : (ssl->mac_flags & SSL_MAC_FLAG_READ_MAC_TLSTREE); int t; if (sending) { @@ -1348,6 +1373,11 @@ int tls1_mac(SSL *ssl, SSL3_RECORD *rec, unsigned char *md, int sending) mac_ctx = hmac; } + if (!SSL_IS_DTLS(ssl) && tlstree_mac && EVP_MD_CTX_ctrl(mac_ctx, EVP_MD_CTRL_TLSTREE, 0, seq) <= 0) { + EVP_MD_CTX_free(hmac); + return 0; + } + if (SSL_IS_DTLS(ssl)) { unsigned char dtlsseq[8], *p = dtlsseq; diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 2b49e7e51a..054fc468ed 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -2687,6 +2687,38 @@ static SSL_CIPHER ssl3_ciphers[] = { 0, 0, }, + { + 1, + "GOST2012-KUZNYECHIK-KUZNYECHIKOMAC", + NULL, + 0x0300C100, + SSL_kGOST18, + SSL_aGOST12, + SSL_KUZNYECHIK, + SSL_KUZNYECHIKOMAC, + TLS1_2_VERSION, TLS1_2_VERSION, + 0, 0, + SSL_HIGH, + SSL_HANDSHAKE_MAC_GOST12_256 | TLS1_PRF_GOST12_256 | TLS1_TLSTREE, + 256, + 256, + }, + { + 1, + "GOST2012-MAGMA-MAGMAOMAC", + NULL, + 0x0300C101, + SSL_kGOST18, + SSL_aGOST12, + SSL_MAGMA, + SSL_MAGMAOMAC, + TLS1_2_VERSION, TLS1_2_VERSION, + 0, 0, + SSL_HIGH, + SSL_HANDSHAKE_MAC_GOST12_256 | TLS1_PRF_GOST12_256 | TLS1_TLSTREE, + 256, + 256, + }, #endif /* OPENSSL_NO_GOST */ #ifndef OPENSSL_NO_IDEA @@ -4374,11 +4406,17 @@ int ssl3_get_req_cert_type(SSL *s, WPACKET *pkt) #ifndef OPENSSL_NO_GOST if (s->version >= TLS1_VERSION && (alg_k & SSL_kGOST)) - return WPACKET_put_bytes_u8(pkt, TLS_CT_GOST01_SIGN) - && WPACKET_put_bytes_u8(pkt, TLS_CT_GOST12_IANA_SIGN) - && WPACKET_put_bytes_u8(pkt, TLS_CT_GOST12_IANA_512_SIGN) - && WPACKET_put_bytes_u8(pkt, TLS_CT_GOST12_LEGACY_SIGN) - && WPACKET_put_bytes_u8(pkt, TLS_CT_GOST12_LEGACY_512_SIGN); + if (!WPACKET_put_bytes_u8(pkt, TLS_CT_GOST01_SIGN) + || !WPACKET_put_bytes_u8(pkt, TLS_CT_GOST12_IANA_SIGN) + || !WPACKET_put_bytes_u8(pkt, TLS_CT_GOST12_IANA_512_SIGN) + || !WPACKET_put_bytes_u8(pkt, TLS_CT_GOST12_LEGACY_SIGN) + || !WPACKET_put_bytes_u8(pkt, TLS_CT_GOST12_LEGACY_512_SIGN)) + return 0; + + if (s->version >= TLS1_2_VERSION && (alg_k & SSL_kGOST18)) + if (!WPACKET_put_bytes_u8(pkt, TLS_CT_GOST12_IANA_SIGN) + || !WPACKET_put_bytes_u8(pkt, TLS_CT_GOST12_IANA_512_SIGN)) + return 0; #endif if ((s->version == SSL3_VERSION) && (alg_k & SSL_kDHE)) { diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c index 7b3a5e7c89..ec2dabc89a 100644 --- a/ssl/ssl_ciph.c +++ b/ssl/ssl_ciph.c @@ -56,6 +56,8 @@ static const ssl_cipher_table ssl_cipher_table_cipher[SSL_ENC_NUM_IDX] = { {SSL_CHACHA20POLY1305, NID_chacha20_poly1305}, /* SSL_ENC_CHACHA_IDX 19 */ {SSL_ARIA128GCM, NID_aria_128_gcm}, /* SSL_ENC_ARIA128GCM_IDX 20 */ {SSL_ARIA256GCM, NID_aria_256_gcm}, /* SSL_ENC_ARIA256GCM_IDX 21 */ + {SSL_MAGMA, NID_magma_ctr_acpkm}, /* SSL_ENC_MAGMA_IDX */ + {SSL_KUZNYECHIK, NID_kuznyechik_ctr_acpkm}, /* SSL_ENC_KUZNYECHIK_IDX */ }; #define SSL_COMP_NULL_IDX 0 @@ -81,7 +83,9 @@ static const ssl_cipher_table ssl_cipher_table_mac[SSL_MD_NUM_IDX] = { {SSL_GOST12_512, NID_id_GostR3411_2012_512}, /* SSL_MD_GOST12_512_IDX 8 */ {0, NID_md5_sha1}, /* SSL_MD_MD5_SHA1_IDX 9 */ {0, NID_sha224}, /* SSL_MD_SHA224_IDX 10 */ - {0, NID_sha512} /* SSL_MD_SHA512_IDX 11 */ + {0, NID_sha512}, /* SSL_MD_SHA512_IDX 11 */ + {SSL_MAGMAOMAC, NID_magma_mac}, /* sSL_MD_MAGMAOMAC_IDX */ + {SSL_KUZNYECHIKOMAC, NID_kuznyechik_mac} /* SSL_MD_KUZNYECHIKOMAC_IDX */ }; /* *INDENT-OFF* */ @@ -95,6 +99,7 @@ static const ssl_cipher_table ssl_cipher_table_kx[] = { {SSL_kPSK, NID_kx_psk}, {SSL_kSRP, NID_kx_srp}, {SSL_kGOST, NID_kx_gost}, + {SSL_kGOST18, NID_kx_gost18}, {SSL_kANY, NID_kx_any} }; @@ -138,8 +143,8 @@ static int ssl_mac_pkey_id[SSL_MD_NUM_IDX] = { EVP_PKEY_HMAC, EVP_PKEY_HMAC, EVP_PKEY_HMAC, NID_undef, /* GOST2012_512 */ EVP_PKEY_HMAC, - /* MD5/SHA1, SHA224, SHA512 */ - NID_undef, NID_undef, NID_undef + /* MD5/SHA1, SHA224, SHA512, MAGMAOMAC, KUZNYECHIKOMAC */ + NID_undef, NID_undef, NID_undef, NID_undef, NID_undef }; #define CIPHER_ADD 1 @@ -193,6 +198,7 @@ static const SSL_CIPHER cipher_aliases[] = { {0, SSL_TXT_kDHEPSK, NULL, 0, SSL_kDHEPSK}, {0, SSL_TXT_kSRP, NULL, 0, SSL_kSRP}, {0, SSL_TXT_kGOST, NULL, 0, SSL_kGOST}, + {0, SSL_TXT_kGOST18, NULL, 0, SSL_kGOST18}, /* server authentication aliases */ {0, SSL_TXT_aRSA, NULL, 0, 0, SSL_aRSA}, @@ -226,7 +232,8 @@ static const SSL_CIPHER cipher_aliases[] = { {0, SSL_TXT_IDEA, NULL, 0, 0, 0, SSL_IDEA}, {0, SSL_TXT_SEED, NULL, 0, 0, 0, SSL_SEED}, {0, SSL_TXT_eNULL, NULL, 0, 0, 0, SSL_eNULL}, - {0, SSL_TXT_GOST, NULL, 0, 0, 0, SSL_eGOST2814789CNT | SSL_eGOST2814789CNT12}, + {0, SSL_TXT_GOST, NULL, 0, 0, 0, + SSL_eGOST2814789CNT | SSL_eGOST2814789CNT12 | SSL_MAGMA | SSL_KUZNYECHIK}, {0, SSL_TXT_AES128, NULL, 0, 0, 0, SSL_AES128 | SSL_AES128GCM | SSL_AES128CCM | SSL_AES128CCM8}, {0, SSL_TXT_AES256, NULL, 0, 0, 0, @@ -381,24 +388,38 @@ int ssl_load_ciphers(SSL_CTX *ctx) * Check for presence of GOST 34.10 algorithms, and if they are not * present, disable appropriate auth and key exchange */ - ssl_mac_pkey_id[SSL_MD_GOST89MAC_IDX] = get_optional_pkey_id("gost-mac"); + ssl_mac_pkey_id[SSL_MD_GOST89MAC_IDX] = get_optional_pkey_id(SN_id_Gost28147_89_MAC); if (ssl_mac_pkey_id[SSL_MD_GOST89MAC_IDX]) ctx->ssl_mac_secret_size[SSL_MD_GOST89MAC_IDX] = 32; else disabled_mac_mask |= SSL_GOST89MAC; ssl_mac_pkey_id[SSL_MD_GOST89MAC12_IDX] = - get_optional_pkey_id("gost-mac-12"); + get_optional_pkey_id(SN_gost_mac_12); if (ssl_mac_pkey_id[SSL_MD_GOST89MAC12_IDX]) ctx->ssl_mac_secret_size[SSL_MD_GOST89MAC12_IDX] = 32; else disabled_mac_mask |= SSL_GOST89MAC12; - if (!get_optional_pkey_id("gost2001")) + ssl_mac_pkey_id[SSL_MD_MAGMAOMAC_IDX] = + get_optional_pkey_id(SN_magma_mac); + if (ssl_mac_pkey_id[SSL_MD_MAGMAOMAC_IDX]) + ctx->ssl_mac_secret_size[SSL_MD_MAGMAOMAC_IDX] = 32; + else + disabled_mac_mask |= SSL_MAGMAOMAC; + + ssl_mac_pkey_id[SSL_MD_KUZNYECHIKOMAC_IDX] = + get_optional_pkey_id(SN_kuznyechik_mac); + if (ssl_mac_pkey_id[SSL_MD_KUZNYECHIKOMAC_IDX]) + ctx->ssl_mac_secret_size[SSL_MD_KUZNYECHIKOMAC_IDX] = 32; + else + disabled_mac_mask |= SSL_KUZNYECHIKOMAC; + + if (!get_optional_pkey_id(SN_id_GostR3410_2001)) disabled_auth_mask |= SSL_aGOST01 | SSL_aGOST12; - if (!get_optional_pkey_id("gost2012_256")) + if (!get_optional_pkey_id(SN_id_GostR3410_2012_256)) disabled_auth_mask |= SSL_aGOST12; - if (!get_optional_pkey_id("gost2012_512")) + if (!get_optional_pkey_id(SN_id_GostR3410_2012_512)) disabled_auth_mask |= SSL_aGOST12; /* * Disable GOST key exchange if no GOST signature algs are available * @@ -407,6 +428,9 @@ int ssl_load_ciphers(SSL_CTX *ctx) (SSL_aGOST01 | SSL_aGOST12)) disabled_mkey_mask |= SSL_kGOST; + if ((disabled_auth_mask & SSL_aGOST12) == SSL_aGOST12) + disabled_mkey_mask |= SSL_kGOST18; + return 1; } @@ -1695,6 +1719,9 @@ char *SSL_CIPHER_description(const SSL_CIPHER *cipher, char *buf, int len) case SSL_kGOST: kx = "GOST"; break; + case SSL_kGOST18: + kx = "GOST18"; + break; case SSL_kANY: kx = "any"; break; @@ -1798,6 +1825,12 @@ char *SSL_CIPHER_description(const SSL_CIPHER *cipher, char *buf, int len) case SSL_eGOST2814789CNT12: enc = "GOST89(256)"; break; + case SSL_MAGMA: + enc = "MAGMA"; + break; + case SSL_KUZNYECHIK: + enc = "KUZNYECHIK"; + break; case SSL_CHACHA20POLY1305: enc = "CHACHA20/POLY1305(256)"; break; diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 85d9dd8448..30643c33b4 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -14,6 +14,8 @@ #ifndef OPENSSL_NO_ERR static const ERR_STRING_DATA SSL_str_reasons[] = { + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ALGORITHM_FETCH_FAILED), + "algorithm fetch failed"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY), "application data after close notify"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_APP_DATA_IN_HANDSHAKE), @@ -171,8 +173,6 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { "ext length mismatch"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_FAILED_TO_INIT_ASYNC), "failed to init async"}, - {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ALGORITHM_FETCH_FAILED), - "algorithm fetch failed"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_FRAGMENTED_CLIENT_HELLO), "fragmented client hello"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOT_A_FIN_BEFORE_A_CCS), diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index fef50eea7f..dafec3d5c7 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -3495,11 +3495,11 @@ void ssl_set_masks(SSL *s) #ifndef OPENSSL_NO_GOST if (ssl_has_cert(s, SSL_PKEY_GOST12_512)) { - mask_k |= SSL_kGOST; + mask_k |= SSL_kGOST | SSL_kGOST18; mask_a |= SSL_aGOST12; } if (ssl_has_cert(s, SSL_PKEY_GOST12_256)) { - mask_k |= SSL_kGOST; + mask_k |= SSL_kGOST | SSL_kGOST18; mask_a |= SSL_aGOST12; } if (ssl_has_cert(s, SSL_PKEY_GOST01)) { diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index e938504d3e..083141931c 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -180,6 +180,8 @@ # define SSL_kRSAPSK 0x00000040U # define SSL_kECDHEPSK 0x00000080U # define SSL_kDHEPSK 0x00000100U +/* GOST KDF key exchange, draft-smyshlyaev-tls12-gost-suites */ +# define SSL_kGOST18 0x00000200U /* all PSK */ @@ -234,6 +236,8 @@ # define SSL_CHACHA20POLY1305 0x00080000U # define SSL_ARIA128GCM 0x00100000U # define SSL_ARIA256GCM 0x00200000U +# define SSL_MAGMA 0x00400000U +# define SSL_KUZNYECHIK 0x00800000U # define SSL_AESGCM (SSL_AES128GCM | SSL_AES256GCM) # define SSL_AESCCM (SSL_AES128CCM | SSL_AES256CCM | SSL_AES128CCM8 | SSL_AES256CCM8) @@ -256,6 +260,8 @@ # define SSL_GOST12_256 0x00000080U # define SSL_GOST89MAC12 0x00000100U # define SSL_GOST12_512 0x00000200U +# define SSL_MAGMAOMAC 0x00000400U +# define SSL_KUZNYECHIKOMAC 0x00000800U /* * When adding new digest in the ssl_ciph.c and increment SSL_MD_NUM_IDX make @@ -274,7 +280,9 @@ # define SSL_MD_MD5_SHA1_IDX 9 # define SSL_MD_SHA224_IDX 10 # define SSL_MD_SHA512_IDX 11 -# define SSL_MAX_DIGEST 12 +# define SSL_MD_MAGMAOMAC_IDX 12 +# define SSL_MD_KUZNYECHIKOMAC_IDX 13 +# define SSL_MAX_DIGEST 14 #define SSL_MD_NUM_IDX SSL_MAX_DIGEST @@ -305,6 +313,11 @@ * goes into algorithm2) */ # define TLS1_STREAM_MAC 0x10000 +/* + * TLSTREE cipher/mac key derivation from draft-smyshlyaev-tls12-gost-suites + * (currently this also goes into algorithm2) + */ +# define TLS1_TLSTREE 0x20000 # define SSL_STRONG_MASK 0x0000001FU # define SSL_DEFAULT_MASK 0X00000020U @@ -413,7 +426,9 @@ # define SSL_ENC_CHACHA_IDX 19 # define SSL_ENC_ARIA128GCM_IDX 20 # define SSL_ENC_ARIA256GCM_IDX 21 -# define SSL_ENC_NUM_IDX 22 +# define SSL_ENC_MAGMA_IDX 22 +# define SSL_ENC_KUZNYECHIK_IDX 23 +# define SSL_ENC_NUM_IDX 24 /*- * SSL_kRSA <- RSA_ENC 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, diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c index 03eb050963..7f908f3b4c 100644 --- a/ssl/t1_enc.c +++ b/ssl/t1_enc.c @@ -180,6 +180,11 @@ int tls1_change_cipher_state(SSL *s, int which) else s->mac_flags &= ~SSL_MAC_FLAG_READ_MAC_STREAM; + if (s->s3.tmp.new_cipher->algorithm2 & TLS1_TLSTREE) + s->mac_flags |= SSL_MAC_FLAG_READ_MAC_TLSTREE; + else + s->mac_flags &= ~SSL_MAC_FLAG_READ_MAC_TLSTREE; + if (s->enc_read_ctx != NULL) { reuse_dd = 1; } else if ((s->enc_read_ctx = EVP_CIPHER_CTX_new()) == NULL) { @@ -230,6 +235,11 @@ int tls1_change_cipher_state(SSL *s, int which) s->mac_flags |= SSL_MAC_FLAG_WRITE_MAC_STREAM; else s->mac_flags &= ~SSL_MAC_FLAG_WRITE_MAC_STREAM; + + if (s->s3.tmp.new_cipher->algorithm2 & TLS1_TLSTREE) + s->mac_flags |= SSL_MAC_FLAG_WRITE_MAC_TLSTREE; + else + s->mac_flags &= ~SSL_MAC_FLAG_WRITE_MAC_TLSTREE; if (s->enc_write_ctx != NULL && !SSL_IS_DTLS(s)) { reuse_dd = 1; } else if ((s->enc_write_ctx = EVP_CIPHER_CTX_new()) == NULL) { @@ -617,6 +627,10 @@ size_t tls1_final_finish_mac(SSL *s, const char *str, size_t slen, { size_t hashlen; unsigned char hash[EVP_MAX_MD_SIZE]; + size_t finished_size = TLS1_FINISH_MAC_LENGTH; + + if (s->s3.tmp.new_cipher->algorithm_mkey & SSL_kGOST18) + finished_size = 32; if (!ssl3_digest_cached_records(s, 0)) { /* SSLfatal() already called */ @@ -630,12 +644,12 @@ size_t tls1_final_finish_mac(SSL *s, const char *str, size_t slen, if (!tls1_PRF(s, str, slen, hash, hashlen, NULL, 0, NULL, 0, NULL, 0, s->session->master_key, s->session->master_key_length, - out, TLS1_FINISH_MAC_LENGTH, 1)) { + out, finished_size, 1)) { /* SSLfatal() already called */ return 0; } OPENSSL_cleanse(hash, hashlen); - return TLS1_FINISH_MAC_LENGTH; + return finished_size; } int tls1_generate_master_secret(SSL *s, unsigned char *out, unsigned char *p, diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 416ba28fb6..f3373dc6d5 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1001,6 +1001,21 @@ static const SIGALG_LOOKUP *tls1_get_legacy_sigalg(const SSL *s, int idx) } } } + /* + * As both SSL_PKEY_GOST12_512 and SSL_PKEY_GOST12_256 indices can be used + * with new (aGOST12-only) ciphersuites, we should find out which one is available really. + */ + else if (idx == SSL_PKEY_GOST12_256) { + int real_idx; + + for (real_idx = SSL_PKEY_GOST12_512; real_idx >= SSL_PKEY_GOST12_256; + real_idx--) { + if (s->cert->pkeys[real_idx].privatekey != NULL) { + idx = real_idx; + break; + } + } + } } else { idx = s->cert->key - s->cert->pkeys; } @@ -1794,7 +1809,7 @@ static int tls12_sigalg_allowed(const SSL *s, int op, const SIGALG_LOOKUP *lu) if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_SUPPORTED, 0)) continue; - if ((c->algorithm_mkey & SSL_kGOST) != 0) + if ((c->algorithm_mkey & (SSL_kGOST | SSL_kGOST18)) != 0) break; } if (i == num) diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c index 9f018ce1ad..72e7b376c0 100644 --- a/ssl/t1_trce.c +++ b/ssl/t1_trce.c @@ -444,6 +444,9 @@ static const ssl_trace_tbl ssl_ciphers_tbl[] = { {0xFEFF, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"}, {0xFF85, "LEGACY-GOST2012-GOST8912-GOST8912"}, {0xFF87, "GOST2012-NULL-GOST12"}, + {0xC100, "GOST2012-KUZNYECHIK-KUZNYECHIKOMAC"}, + {0xC101, "GOST2012-MAGMA-MAGMAOMAC"}, + {0xC102, "GOST2012-GOST8912-IANA"}, }; /* Compression methods */ @@ -593,7 +596,9 @@ static const ssl_trace_tbl ssl_ctype_tbl[] = { {20, "fortezza_dms"}, {64, "ecdsa_sign"}, {65, "rsa_fixed_ecdh"}, - {66, "ecdsa_fixed_ecdh"} + {66, "ecdsa_fixed_ecdh"}, + {67, "gost_sign256"}, + {68, "gost_sign512"}, }; static const ssl_trace_tbl ssl_psk_kex_modes_tbl[] = { @@ -1078,6 +1083,10 @@ static int ssl_get_keyex(const char **pname, const SSL *ssl) *pname = "GOST"; return SSL_kGOST; } + if (alg_k & SSL_kGOST18) { + *pname = "GOST18"; + return SSL_kGOST18; + } *pname = "UNKNOWN"; return 0; } @@ -1124,7 +1133,11 @@ static int ssl_print_client_keyex(BIO *bio, int indent, const SSL *ssl, ssl_print_hex(bio, indent + 2, "GostKeyTransportBlob", msg, msglen); msglen = 0; break; - + case SSL_kGOST18: + ssl_print_hex(bio, indent + 2, + "GOST-wrapped PreMasterSecret", msg, msglen); + msglen = 0; + break; } return !msglen; -- 2.34.1