diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 07:33:12 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 07:33:12 +0000 |
commit | 36082a2fe36ecd800d784ae44c14f1f18c66a7e9 (patch) | |
tree | 6c68e0c0097987aff85a01dabddd34b862309a7c /lib/vko.c | |
parent | Initial commit. (diff) | |
download | gnutls28-upstream.tar.xz gnutls28-upstream.zip |
Adding upstream version 3.7.9.upstream/3.7.9upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/vko.c')
-rw-r--r-- | lib/vko.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/lib/vko.c b/lib/vko.c new file mode 100644 index 0000000..e0d4f04 --- /dev/null +++ b/lib/vko.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2011-2012 Free Software Foundation, Inc. + * Copyright (C) 2016 Dmitry Eremin-Solenikov + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + */ + +/* + * This is split from main TLS key exchange, because it might be useful in + * future for S/MIME support. For the definition of the algorithm see RFC 4357, + * section 5.2. + */ +#include "gnutls_int.h" +#include "vko.h" +#include "pk.h" +#include "common.h" + +static int +_gnutls_gost_vko_key(gnutls_pk_params_st *pub, + gnutls_pk_params_st *priv, + gnutls_datum_t *ukm, + gnutls_digest_algorithm_t digalg, + gnutls_datum_t *kek) +{ + gnutls_datum_t tmp_vko_key; + int ret; + + ret = _gnutls_pk_derive_nonce(pub->algo, &tmp_vko_key, + priv, pub, ukm); + if (ret < 0) + return gnutls_assert_val(ret); + + kek->size = gnutls_hash_get_len(digalg); + kek->data = gnutls_malloc(kek->size); + if (kek->data == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + ret = gnutls_hash_fast(digalg, tmp_vko_key.data, tmp_vko_key.size, kek->data); + if (ret < 0) { + gnutls_assert(); + _gnutls_free_datum(kek); + goto cleanup; + } + + ret = 0; + +cleanup: + _gnutls_free_temp_key_datum(&tmp_vko_key); + + return ret; +} + +static const gnutls_datum_t zero_data = { NULL, 0 }; + +int +_gnutls_gost_keytrans_encrypt(gnutls_pk_params_st *pub, + gnutls_pk_params_st *priv, + gnutls_datum_t *cek, + gnutls_datum_t *ukm, + gnutls_datum_t *out) +{ + int ret; + gnutls_datum_t kek; + gnutls_datum_t enc, imit; + gnutls_digest_algorithm_t digalg; + asn1_node kx; + + if (pub->algo == GNUTLS_PK_GOST_01) + digalg = GNUTLS_DIG_GOSTR_94; + else + digalg = GNUTLS_DIG_STREEBOG_256; + + ret = _gnutls_gost_vko_key(pub, priv, ukm, digalg, &kek); + if (ret < 0) { + gnutls_assert(); + + return ret; + } + + ret = _gnutls_gost_key_wrap(pub->gost_params, &kek, ukm, cek, + &enc, &imit); + _gnutls_free_key_datum(&kek); + if (ret < 0) { + gnutls_assert(); + + return ret; + } + + ret = asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.GostR3410-KeyTransport", + &kx); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + _gnutls_free_datum(&enc); + _gnutls_free_datum(&imit); + + return ret; + } + + ret = _gnutls_x509_write_value(kx, "transportParameters.ukm", ukm); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_encode_and_copy_PKI_params(kx, + "transportParameters.ephemeralPublicKey", + priv); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if ((ret = asn1_write_value(kx, "transportParameters.encryptionParamSet", + gnutls_gost_paramset_get_oid(pub->gost_params), + 1)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + ret = _gnutls_x509_write_value(kx, "sessionEncryptedKey.encryptedKey", &enc); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_write_value(kx, "sessionEncryptedKey.maskKey", &zero_data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + ret = _gnutls_x509_write_value(kx, "sessionEncryptedKey.macKey", &imit); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_der_encode(kx, "", out, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + +cleanup: + asn1_delete_structure(&kx); + _gnutls_free_datum(&enc); + _gnutls_free_datum(&imit); + + return ret; +} + +int +_gnutls_gost_keytrans_decrypt(gnutls_pk_params_st *priv, + gnutls_datum_t *cek, + gnutls_datum_t *ukm, + gnutls_datum_t *out) +{ + int ret; + asn1_node kx; + gnutls_pk_params_st pub; + gnutls_datum_t kek; + gnutls_datum_t ukm2, enc, imit; + char oid[MAX_OID_SIZE]; + int oid_size; + gnutls_digest_algorithm_t digalg; + + if ((ret = asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.GostR3410-KeyTransport", + &kx)) != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + + return ret; + } + + ret = _asn1_strict_der_decode(&kx, cek->data, cek->size, NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + asn1_delete_structure(&kx); + + return ret; + } + + ret = _gnutls_get_asn_mpis(kx, + "transportParameters.ephemeralPublicKey", + &pub); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (pub.algo != priv->algo || + pub.gost_params != priv->gost_params || + pub.curve != priv->curve) { + gnutls_assert(); + ret = GNUTLS_E_ILLEGAL_PARAMETER; + goto cleanup; + } + + oid_size = sizeof(oid); + ret = asn1_read_value(kx, "transportParameters.encryptionParamSet", oid, &oid_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + if (gnutls_oid_to_gost_paramset(oid) != priv->gost_params) { + gnutls_assert(); + ret = GNUTLS_E_ASN1_DER_ERROR; + goto cleanup; + } + + ret = _gnutls_x509_read_value(kx, "transportParameters.ukm", &ukm2); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Kind of strange design. For TLS UKM is calculated as a hash of + * client and server random. At the same time UKM is transmitted as a + * part of KeyTransport structure. At this point we have to compare + * them to check that they are equal. This does not result in an oracle + * of any kind as all values are transmitted in cleartext. Returning + * that this point won't give any information to the attacker. + */ + if (ukm2.size != ukm->size || memcmp(ukm2.data, ukm->data, ukm->size) != 0) { + gnutls_assert(); + _gnutls_free_datum(&ukm2); + ret = GNUTLS_E_DECRYPTION_FAILED; + goto cleanup; + } + _gnutls_free_datum(&ukm2); + + ret = _gnutls_x509_read_value(kx, "sessionEncryptedKey.encryptedKey", + &enc); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_read_value(kx, "sessionEncryptedKey.macKey", + &imit); + if (ret < 0) { + gnutls_assert(); + _gnutls_free_datum(&enc); + goto cleanup; + } + + if (pub.algo == GNUTLS_PK_GOST_01) + digalg = GNUTLS_DIG_GOSTR_94; + else + digalg = GNUTLS_DIG_STREEBOG_256; + + ret = _gnutls_gost_vko_key(&pub, priv, ukm, digalg, &kek); + if (ret < 0) { + gnutls_assert(); + goto cleanup2; + } + + ret = _gnutls_gost_key_unwrap(pub.gost_params, &kek, ukm, + &enc, &imit, out); + _gnutls_free_key_datum(&kek); + + if (ret < 0) { + gnutls_assert(); + goto cleanup2; + } + + ret = 0; + +cleanup2: + _gnutls_free_datum(&imit); + _gnutls_free_datum(&enc); +cleanup: + gnutls_pk_params_release(&pub); + asn1_delete_structure(&kx); + + return ret; +} |