]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Add support for VKO GOST key exchange
authorDmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Thu, 27 Oct 2016 00:30:34 +0000 (03:30 +0300)
committerDmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Fri, 8 Nov 2019 09:53:15 +0000 (12:53 +0300)
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
devel/libgnutls-latest-x86_64.abi
lib/algorithms/kx.c
lib/algorithms/publickey.c
lib/auth/Makefile.am
lib/auth/vko_gost.c [new file with mode: 0644]
lib/includes/gnutls/gnutls.h.in

index af21b7d548f3ce4850ab4375bd679c3c445b098d..cd7467d067c6a2055906ba45b6bc12736b6e04e9 100644 (file)
       <enumerator name='GNUTLS_KX_ECDHE_ECDSA' value='13'/>
       <enumerator name='GNUTLS_KX_ECDHE_PSK' value='14'/>
       <enumerator name='GNUTLS_KX_RSA_PSK' value='15'/>
+      <enumerator name='GNUTLS_KX_VKO_GOST_12' value='16'/>
     </enum-decl>
     <typedef-decl name='gnutls_kx_algorithm_t' type-id='type-id-43' id='type-id-30'/>
     <enum-decl name='__anonymous_enum__' is-anonymous='yes' id='type-id-44'>
index 9b670693b46438128ecd2dcd97a9ed99e52420d3..2591ec193a9760e84943526ab610b75608255dc1 100644 (file)
@@ -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}
 };
 
@@ -121,6 +123,9 @@ static const gnutls_kx_algo_entry _gnutls_kx_algorithms[] = {
 #endif
 #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
index f1d7c1e89eff60fbe46d2ad3befcbdf9d6256919..dc535c2f6527f693aea523f3d98380680c065928 100644 (file)
@@ -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}
 };
 
index 6997eebe8d7283c967780c9aacb947ba08849d30..e85eaaef813c6eaeeacf84834d73750fb12bfc58 100644 (file)
@@ -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 (file)
index 0000000..35a4a2b
--- /dev/null
@@ -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
index cfd84d6e2da15a87fca292b813e818ef02b05956..d8464c94da80f0fe9fa91d9fd3335e444d4f001d 100644 (file)
@@ -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;
 
 /**