From e2409a33a5ffb21c41de9f213fecaf26e384d6a8 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov 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 --- 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