summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDmitry Eremin-Solenikov <dbaryshkov@gmail.com>2016-10-27 03:30:34 +0300
committerDmitry Eremin-Solenikov <dbaryshkov@gmail.com>2019-11-08 12:53:15 +0300
commit6eadc827ea6323cf7d752cba1b41cf9b93aae9b9 (patch)
tree3b83c6e2b08c35b8e1b584a09975f69fa2a077ce /lib
parent1412ef059395e2b404eb4e3c018d9cca4e790a98 (diff)
downloadgnutls-6eadc827ea6323cf7d752cba1b41cf9b93aae9b9.tar.gz
Add support for VKO GOST key exchange
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/algorithms/kx.c5
-rw-r--r--lib/algorithms/publickey.c3
-rw-r--r--lib/auth/Makefile.am2
-rw-r--r--lib/auth/vko_gost.c323
-rw-r--r--lib/includes/gnutls/gnutls.h.in4
5 files changed, 335 insertions, 2 deletions
diff --git a/lib/algorithms/kx.c b/lib/algorithms/kx.c
index 9b670693b4..2591ec193a 100644
--- a/lib/algorithms/kx.c
+++ b/lib/algorithms/kx.c
@@ -41,6 +41,7 @@ extern mod_auth_st dhe_psk_auth_struct;
extern mod_auth_st rsa_psk_auth_struct;
extern mod_auth_st srp_rsa_auth_struct;
extern mod_auth_st srp_dss_auth_struct;
+extern mod_auth_st vko_gost_auth_struct;
/* Cred type mappings to KX algorithms
@@ -73,6 +74,7 @@ static const gnutls_cred_map cred_mappings[] = {
{GNUTLS_KX_SRP_DSS, GNUTLS_CRD_SRP, GNUTLS_CRD_CERTIFICATE},
{GNUTLS_KX_ANON_DH, GNUTLS_CRD_ANON, GNUTLS_CRD_ANON},
{GNUTLS_KX_ANON_ECDH, GNUTLS_CRD_ANON, GNUTLS_CRD_ANON},
+ {GNUTLS_KX_VKO_GOST_12, GNUTLS_CRD_CERTIFICATE, GNUTLS_CRD_CERTIFICATE},
{0, 0, 0}
};
@@ -122,6 +124,9 @@ static const gnutls_kx_algo_entry _gnutls_kx_algorithms[] = {
#if defined(ENABLE_ANON) && defined(ENABLE_ECDHE)
{"ANON-ECDH", GNUTLS_KX_ANON_ECDH, &anon_ecdh_auth_struct, 0, 0},
#endif
+#ifdef ENABLE_GOST
+ {"VKO-GOST-12", GNUTLS_KX_VKO_GOST_12, &vko_gost_auth_struct, 0, 0},
+#endif
/* for deprecated and legacy algorithms no longer supported, use
* GNUTLS_KX_INVALID as an entry. This will make them available
* as priority strings, but they will be a no-op.
diff --git a/lib/algorithms/publickey.c b/lib/algorithms/publickey.c
index f1d7c1e89e..dc535c2f65 100644
--- a/lib/algorithms/publickey.c
+++ b/lib/algorithms/publickey.c
@@ -56,6 +56,9 @@ static const gnutls_pk_map pk_mappings[] = {
{GNUTLS_KX_ECDHE_RSA, GNUTLS_PK_RSA_PSS, CIPHER_SIGN},
{GNUTLS_KX_SRP_DSS, GNUTLS_PK_DSA, CIPHER_SIGN},
{GNUTLS_KX_RSA_PSK, GNUTLS_PK_RSA, CIPHER_ENCRYPT},
+ {GNUTLS_KX_VKO_GOST_12, GNUTLS_PK_GOST_01, CIPHER_SIGN},
+ {GNUTLS_KX_VKO_GOST_12, GNUTLS_PK_GOST_12_256, CIPHER_SIGN},
+ {GNUTLS_KX_VKO_GOST_12, GNUTLS_PK_GOST_12_512, CIPHER_SIGN},
{0, 0, 0}
};
diff --git a/lib/auth/Makefile.am b/lib/auth/Makefile.am
index 6997eebe8d..e85eaaef81 100644
--- a/lib/auth/Makefile.am
+++ b/lib/auth/Makefile.am
@@ -38,4 +38,4 @@ libgnutls_auth_la_SOURCES = anon.c cert.c dh_common.c dhe.c \
rsa_psk.c dhe_psk.c psk.c psk_passwd.c rsa.c srp_kx.c \
srp_passwd.c srp_rsa.c srp_sb64.c anon.h cert.h dh_common.h \
psk.h psk_passwd.h srp_kx.h srp_passwd.h anon_ecdh.c \
- ecdhe.c ecdhe.h rsa_common.h
+ ecdhe.c ecdhe.h rsa_common.h vko_gost.c
diff --git a/lib/auth/vko_gost.c b/lib/auth/vko_gost.c
new file mode 100644
index 0000000000..35a4a2b0fb
--- /dev/null
+++ b/lib/auth/vko_gost.c
@@ -0,0 +1,323 @@
+/*
+ * 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/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "auth.h"
+#include "errors.h"
+#include "vko.h"
+#include <state.h>
+#include <datum.h>
+#include <ext/signature.h>
+#include <ext/supported_groups.h>
+#include <auth/cert.h>
+#include <pk.h>
+#include <abstract_int.h>
+
+#if defined(ENABLE_GOST)
+static int gen_vko_gost_client_kx(gnutls_session_t, gnutls_buffer_st *);
+static int proc_vko_gost_client_kx(gnutls_session_t session,
+ uint8_t * data, size_t _data_size);
+
+/* VKO GOST Key Exchange:
+ * see draft-smyshlyaev-tls12-gost-suites-06, Section 4.2.4
+ *
+ * Client generates ephemeral key pair, uses server's public key (from
+ * certificate), ephemeral private key and additional nonce (UKM) to generate
+ * (VKO) shared point/shared secret. This secret is used to encrypt (key wrap)
+ * random PMS. Then encrypted PMS and client's ephemeral public key are wrappen
+ * in ASN.1 structure and sent in KX message.
+ *
+ * Server uses decodes ASN.1 structure and uses it's own private key and
+ * client's ephemeral public key to unwrap PMS.
+ *
+ * Note, this KX is not PFS one, despite using ephemeral key pairs on client
+ * side.
+ */
+const mod_auth_st vko_gost_auth_struct = {
+ "VKO_GOST",
+ _gnutls_gen_cert_server_crt,
+ _gnutls_gen_cert_client_crt,
+ NULL,
+ gen_vko_gost_client_kx,
+ _gnutls_gen_cert_client_crt_vrfy,
+ _gnutls_gen_cert_server_cert_req,
+
+ _gnutls_proc_crt,
+ _gnutls_proc_crt,
+ NULL,
+ proc_vko_gost_client_kx,
+ _gnutls_proc_cert_client_crt_vrfy,
+ _gnutls_proc_cert_cert_req
+};
+
+#define VKO_GOST_UKM_LEN 8
+
+static int
+calc_ukm(gnutls_session_t session, uint8_t *ukm)
+{
+ gnutls_digest_algorithm_t digalg = GNUTLS_DIG_STREEBOG_256;
+ gnutls_hash_hd_t dig;
+ int ret;
+
+ ret = gnutls_hash_init(&dig, digalg);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ gnutls_hash(dig, session->security_parameters.client_random,
+ sizeof(session->security_parameters.client_random));
+
+ gnutls_hash(dig, session->security_parameters.server_random,
+ sizeof(session->security_parameters.server_random));
+
+ gnutls_hash_deinit(dig, ukm);
+
+ return gnutls_hash_get_len(digalg);
+}
+
+static int print_priv_key(gnutls_pk_params_st *params)
+{
+ int ret;
+ uint8_t priv_buf[512/8];
+ char buf[512 / 4 + 1];
+ size_t bytes = sizeof(priv_buf);
+
+ /* Check if _gnutls_hard_log will print anything */
+ if (likely(_gnutls_log_level < 9))
+ return GNUTLS_E_SUCCESS;
+
+ ret = _gnutls_mpi_print(params->params[GOST_K],
+ priv_buf, &bytes);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ _gnutls_hard_log("INT: VKO PRIVATE KEY[%zd]: %s\n",
+ bytes, _gnutls_bin2hex(priv_buf,
+ bytes,
+ buf, sizeof(buf),
+ NULL));
+ return 0;
+}
+
+static int
+vko_prepare_client_keys(gnutls_session_t session,
+ gnutls_pk_params_st *pub,
+ gnutls_pk_params_st *priv)
+{
+ int ret;
+ gnutls_ecc_curve_t curve;
+ const gnutls_group_entry_st *group;
+ cert_auth_info_t info;
+ gnutls_pcert_st peer_cert;
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+ if (info == NULL || info->ncerts == 0)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = _gnutls_get_auth_info_pcert(&peer_cert,
+ session->security_parameters.
+ server_ctype, info);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Copy public key contents and free the rest */
+ memcpy(pub, &peer_cert.pubkey->params, sizeof(gnutls_pk_params_st));
+ gnutls_free(peer_cert.pubkey);
+ peer_cert.pubkey = NULL;
+ gnutls_pcert_deinit(&peer_cert);
+
+ curve = pub->curve;
+ group = _gnutls_id_to_group(_gnutls_ecc_curve_get_group(curve));
+ if (group == NULL) {
+ _gnutls_debug_log("received unknown curve %d\n", curve);
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ } else {
+ _gnutls_debug_log("received curve %s\n", group->name);
+ }
+
+ ret = _gnutls_session_supports_group(session, group->id);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (pub->algo == GNUTLS_PK_GOST_12_512) {
+ gnutls_sign_algorithm_set_server(session, GNUTLS_SIGN_GOST_512);
+ } else {
+ gnutls_sign_algorithm_set_server(session, GNUTLS_SIGN_GOST_256);
+ }
+
+ _gnutls_session_group_set(session, group);
+
+ ret = _gnutls_pk_generate_keys(pub->algo,
+ curve,
+ priv, 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ priv->gost_params = pub->gost_params;
+
+ print_priv_key(priv);
+
+ session->key.key.size = 32; /* GOST key size */
+ session->key.key.data = gnutls_malloc(session->key.key.size);
+ if (session->key.key.data == NULL) {
+ gnutls_assert();
+ session->key.key.size = 0;
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ /* Generate random */
+ ret = gnutls_rnd(GNUTLS_RND_RANDOM, session->key.key.data,
+ session->key.key.size);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_free(session->key.key.data);
+ session->key.key.size = 0;
+ return ret;
+ }
+
+ return 0;
+}
+
+/* KX message is:
+ TLSGostKeyTransportBlob ::= SEQUENCE {
+ keyBlob GostR3410-KeyTransport,
+ proxyKeyBlobs SEQUENCE OF TLSProxyKeyTransportBlob OPTIONAL
+ }
+
+ draft-smyshlyaev-tls12-gost-suites does not define proxyKeyBlobs, but old
+ CSPs still send additional information after keyBlob.
+
+ We only need keyBlob and we completely ignore the rest of the structure.
+
+ _gnutls_gost_keytrans_decrypt will decrypt GostR3410-KeyTransport
+ */
+
+static int
+proc_vko_gost_client_kx(gnutls_session_t session,
+ uint8_t * data, size_t _data_size)
+{
+ int ret, i = 0;
+ ssize_t data_size = _data_size;
+ gnutls_privkey_t privkey = session->internals.selected_key;
+ uint8_t ukm_data[MAX_HASH_SIZE];
+ gnutls_datum_t ukm = {ukm_data, VKO_GOST_UKM_LEN};
+ gnutls_datum_t cek;
+ int len;
+
+ if (!privkey || privkey->type != GNUTLS_PRIVKEY_X509)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ /* Skip TLSGostKeyTransportBlob tag and length */
+ DECR_LEN(data_size, 1);
+ if (data[0] != (ASN1_TAG_SEQUENCE | ASN1_CLASS_STRUCTURED))
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ i += 1;
+
+ ret = asn1_get_length_der(&data[i], data_size, &len);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
+ DECR_LEN(data_size, len);
+ i += len;
+
+ /* Check that nothing is left after TLSGostKeyTransportBlob */
+ DECR_LEN_FINAL(data_size, ret);
+
+ /* Point data to GostR3410-KeyTransport */
+ data_size = ret;
+ data += i;
+
+ /* Now do the tricky part: determine length of GostR3410-KeyTransport */
+ DECR_LEN(data_size, 1); /* tag */
+ ret = asn1_get_length_der(&data[1], data_size, &len);
+ DECR_LEN_FINAL(data_size, len + ret);
+
+ cek.data = data;
+ cek.size = ret + len + 1;
+
+ ret = calc_ukm(session, ukm_data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_gost_keytrans_decrypt(&privkey->key.x509->params,
+ &cek, &ukm,
+ &session->key.key);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+static int
+gen_vko_gost_client_kx(gnutls_session_t session,
+ gnutls_buffer_st * data)
+{
+ int ret;
+ gnutls_datum_t out = {};
+ uint8_t ukm_data[MAX_HASH_SIZE];
+ gnutls_datum_t ukm = {ukm_data, VKO_GOST_UKM_LEN};
+ gnutls_pk_params_st pub;
+ gnutls_pk_params_st priv;
+ uint8_t tl[1 + ASN1_MAX_LENGTH_SIZE];
+ int len;
+
+ ret = calc_ukm(session, ukm_data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ gnutls_pk_params_init(&pub);
+ gnutls_pk_params_init(&priv);
+ ret = vko_prepare_client_keys(session, &pub, &priv);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_gost_keytrans_encrypt(&pub,
+ &priv,
+ &session->key.key,
+ &ukm, &out);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ tl[0] = ASN1_TAG_SEQUENCE | ASN1_CLASS_STRUCTURED;
+ asn1_length_der(out.size, tl + 1, &len);
+ ret = gnutls_buffer_append_data(data, tl, len + 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_buffer_append_data(data, out.data, out.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = data->length;
+ cleanup:
+ /* no longer needed */
+ gnutls_pk_params_release(&priv);
+ gnutls_pk_params_release(&pub);
+
+ _gnutls_free_datum(&out);
+
+ return ret;
+}
+#endif
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index cfd84d6e2d..d8464c94da 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -206,6 +206,7 @@ typedef enum gnutls_cipher_algorithm {
* @GNUTLS_KX_DHE_PSK: DHE-PSK key-exchange algorithm.
* @GNUTLS_KX_ECDHE_PSK: ECDHE-PSK key-exchange algorithm.
* @GNUTLS_KX_RSA_PSK: RSA-PSK key-exchange algorithm.
+ * @GNUTLS_KX_VKO_GOST_12: VKO GOST R 34.10-2012 key-exchange algorithm.
*
* Enumeration of different key exchange algorithms.
*/
@@ -225,7 +226,8 @@ typedef enum {
GNUTLS_KX_ECDHE_RSA = 12,
GNUTLS_KX_ECDHE_ECDSA = 13,
GNUTLS_KX_ECDHE_PSK = 14,
- GNUTLS_KX_RSA_PSK = 15
+ GNUTLS_KX_RSA_PSK = 15,
+ GNUTLS_KX_VKO_GOST_12 = 16
} gnutls_kx_algorithm_t;
/**