summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDmitry Eremin-Solenikov <dbaryshkov@gmail.com>2019-11-08 23:03:16 +0000
committerDmitry Eremin-Solenikov <dbaryshkov@gmail.com>2019-11-08 23:03:16 +0000
commit3d4928e4268bea40a37e796795e42353d4415a6d (patch)
tree09c47bc9a321b97e42be721cf95951fb909b4227 /lib
parentb5029c0a57ba881e1db4a8c7d5c680f0f2df8dc4 (diff)
parent6eadc827ea6323cf7d752cba1b41cf9b93aae9b9 (diff)
downloadgnutls-3d4928e4268bea40a37e796795e42353d4415a6d.tar.gz
Merge branch 'gost-split-2' into 'master'
GOST key exchange support See merge request gnutls/gnutls!1097
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am8
-rw-r--r--lib/algorithms.h2
-rw-r--r--lib/algorithms/ecc.c39
-rw-r--r--lib/algorithms/groups.c52
-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/crypto-backend.h15
-rw-r--r--lib/gnutls.asn22
-rw-r--r--lib/gnutls_asn1_tab.c19
-rw-r--r--lib/includes/gnutls/gnutls.h.in19
-rw-r--r--lib/nettle/Makefile.am6
-rw-r--r--lib/nettle/gost/gost-wrap.c132
-rw-r--r--lib/nettle/gost/gost28147.h25
-rw-r--r--lib/nettle/gost/gostdsa-vko.c78
-rw-r--r--lib/nettle/gost/gostdsa.h7
-rw-r--r--lib/nettle/gost_keywrap.c121
-rw-r--r--lib/nettle/pk.c61
-rw-r--r--lib/pk.h5
-rw-r--r--lib/vko.c299
-rw-r--r--lib/vko.h38
22 files changed, 1274 insertions, 7 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index f1e3bb90b6..eddd1167a7 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -83,6 +83,10 @@ COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h handshake-tls
hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c cert-cred-rawpk.c \
iov.c iov.h
+if ENABLE_GOST
+COBJECTS += vko.c
+endif
+
if WINDOWS
COBJECTS += system/keys-win.c
else
@@ -131,6 +135,10 @@ if ENABLE_PKCS11
HFILES += pkcs11_int.h pkcs11x.h
endif
+if ENABLE_GOST
+HFILES += vko.h
+endif
+
libgnutls_la_SOURCES = $(HFILES) $(COBJECTS) $(SRP_COBJECTS) \
$(PSK_COBJECTS) \
gnutls.asn pkix.asn libgnutls.map
diff --git a/lib/algorithms.h b/lib/algorithms.h
index 60556a9a96..0d14331154 100644
--- a/lib/algorithms.h
+++ b/lib/algorithms.h
@@ -429,6 +429,7 @@ typedef struct gnutls_ecc_curve_entry_st {
unsigned sig_size; /* the size of curve signatures in bytes (EdDSA) */
unsigned gost_curve;
bool supported;
+ gnutls_group_t group;
} gnutls_ecc_curve_entry_st;
const gnutls_ecc_curve_entry_st
@@ -436,6 +437,7 @@ const gnutls_ecc_curve_entry_st
unsigned _gnutls_ecc_curve_is_supported(gnutls_ecc_curve_t);
+gnutls_group_t _gnutls_ecc_curve_get_group(gnutls_ecc_curve_t);
const gnutls_group_entry_st *_gnutls_tls_id_to_group(unsigned num);
const gnutls_group_entry_st * _gnutls_id_to_group(unsigned id);
diff --git a/lib/algorithms/ecc.c b/lib/algorithms/ecc.c
index 4308e911ad..8b4b78f67d 100644
--- a/lib/algorithms/ecc.c
+++ b/lib/algorithms/ecc.c
@@ -37,6 +37,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "SECP192R1",
.oid = "1.2.840.10045.3.1.1",
.id = GNUTLS_ECC_CURVE_SECP192R1,
+ .group = GNUTLS_GROUP_SECP192R1,
.pk = GNUTLS_PK_ECDSA,
.size = 24,
.supported = 1,
@@ -45,6 +46,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "SECP224R1",
.oid = "1.3.132.0.33",
.id = GNUTLS_ECC_CURVE_SECP224R1,
+ .group = GNUTLS_GROUP_SECP224R1,
.pk = GNUTLS_PK_ECDSA,
.size = 28,
.supported = 1,
@@ -54,6 +56,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "SECP256R1",
.oid = "1.2.840.10045.3.1.7",
.id = GNUTLS_ECC_CURVE_SECP256R1,
+ .group = GNUTLS_GROUP_SECP256R1,
.pk = GNUTLS_PK_ECDSA,
.size = 32,
.supported = 1,
@@ -62,6 +65,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "SECP384R1",
.oid = "1.3.132.0.34",
.id = GNUTLS_ECC_CURVE_SECP384R1,
+ .group = GNUTLS_GROUP_SECP384R1,
.pk = GNUTLS_PK_ECDSA,
.size = 48,
.supported = 1,
@@ -70,6 +74,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "SECP521R1",
.oid = "1.3.132.0.35",
.id = GNUTLS_ECC_CURVE_SECP521R1,
+ .group = GNUTLS_GROUP_SECP521R1,
.pk = GNUTLS_PK_ECDSA,
.size = 66,
.supported = 1,
@@ -77,6 +82,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
{
.name = "X25519",
.id = GNUTLS_ECC_CURVE_X25519,
+ .group = GNUTLS_GROUP_X25519,
.pk = GNUTLS_PK_ECDH_X25519,
.size = 32,
.supported = 1,
@@ -118,6 +124,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "CryptoPro-A",
.oid = "1.2.643.2.2.35.1",
.id = GNUTLS_ECC_CURVE_GOST256CPA,
+ .group = GNUTLS_GROUP_GC256B,
.pk = GNUTLS_PK_UNKNOWN,
.size = 32,
.gost_curve = 1,
@@ -127,6 +134,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "CryptoPro-B",
.oid = "1.2.643.2.2.35.2",
.id = GNUTLS_ECC_CURVE_GOST256CPB,
+ .group = GNUTLS_GROUP_GC256C,
.pk = GNUTLS_PK_UNKNOWN,
.size = 32,
.gost_curve = 1,
@@ -136,6 +144,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "CryptoPro-C",
.oid = "1.2.643.2.2.35.3",
.id = GNUTLS_ECC_CURVE_GOST256CPC,
+ .group = GNUTLS_GROUP_GC256D,
.pk = GNUTLS_PK_UNKNOWN,
.size = 32,
.gost_curve = 1,
@@ -145,6 +154,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "CryptoPro-XchA",
.oid = "1.2.643.2.2.36.0",
.id = GNUTLS_ECC_CURVE_GOST256CPXA,
+ .group = GNUTLS_GROUP_GC256B,
.pk = GNUTLS_PK_UNKNOWN,
.size = 32,
.gost_curve = 1,
@@ -154,6 +164,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "CryptoPro-XchB",
.oid = "1.2.643.2.2.36.1",
.id = GNUTLS_ECC_CURVE_GOST256CPXB,
+ .group = GNUTLS_GROUP_GC256D,
.pk = GNUTLS_PK_UNKNOWN,
.size = 32,
.gost_curve = 1,
@@ -163,6 +174,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "TC26-256-A",
.oid = "1.2.643.7.1.2.1.1.1",
.id = GNUTLS_ECC_CURVE_GOST256A,
+ .group = GNUTLS_GROUP_GC256A,
.pk = GNUTLS_PK_GOST_12_256,
.size = 32,
.gost_curve = 1,
@@ -172,6 +184,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "TC26-256-B",
.oid = "1.2.643.7.1.2.1.1.2",
.id = GNUTLS_ECC_CURVE_GOST256B,
+ .group = GNUTLS_GROUP_GC256B,
.pk = GNUTLS_PK_GOST_12_256,
.size = 32,
.gost_curve = 1,
@@ -181,6 +194,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "TC26-256-C",
.oid = "1.2.643.7.1.2.1.1.3",
.id = GNUTLS_ECC_CURVE_GOST256C,
+ .group = GNUTLS_GROUP_GC256C,
.pk = GNUTLS_PK_GOST_12_256,
.size = 32,
.gost_curve = 1,
@@ -190,6 +204,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "TC26-256-D",
.oid = "1.2.643.7.1.2.1.1.4",
.id = GNUTLS_ECC_CURVE_GOST256D,
+ .group = GNUTLS_GROUP_GC256D,
.pk = GNUTLS_PK_GOST_12_256,
.size = 32,
.gost_curve = 1,
@@ -199,6 +214,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "TC26-512-A",
.oid = "1.2.643.7.1.2.1.2.1",
.id = GNUTLS_ECC_CURVE_GOST512A,
+ .group = GNUTLS_GROUP_GC512A,
.pk = GNUTLS_PK_GOST_12_512,
.size = 64,
.gost_curve = 1,
@@ -208,6 +224,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "TC26-512-B",
.oid = "1.2.643.7.1.2.1.2.2",
.id = GNUTLS_ECC_CURVE_GOST512B,
+ .group = GNUTLS_GROUP_GC512B,
.pk = GNUTLS_PK_GOST_12_512,
.size = 64,
.gost_curve = 1,
@@ -217,6 +234,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = {
.name = "TC26-512-C",
.oid = "1.2.643.7.1.2.1.2.3",
.id = GNUTLS_ECC_CURVE_GOST512C,
+ .group = GNUTLS_GROUP_GC512C,
.pk = GNUTLS_PK_GOST_12_512,
.size = 64,
.gost_curve = 1,
@@ -492,3 +510,24 @@ gnutls_pk_algorithm_t gnutls_ecc_curve_get_pk(gnutls_ecc_curve_t curve)
return ret;
}
+/**
+ * _gnutls_ecc_curve_get_group:
+ * @curve: is an ECC curve
+ *
+ * Returns: the group associated with the named curve or %GNUTLS_GROUP_INVALID.
+ *
+ * Since: 3.6.11
+ */
+gnutls_group_t _gnutls_ecc_curve_get_group(gnutls_ecc_curve_t curve)
+{
+ gnutls_group_t ret = GNUTLS_GROUP_INVALID;
+
+ GNUTLS_ECC_CURVE_LOOP(
+ if (p->id == curve && p->supported && _gnutls_pk_curve_exists(p->id)) {
+ ret = p->group;
+ break;
+ }
+ );
+
+ return ret;
+}
diff --git a/lib/algorithms/groups.c b/lib/algorithms/groups.c
index 1f82bf678a..6e1326666a 100644
--- a/lib/algorithms/groups.c
+++ b/lib/algorithms/groups.c
@@ -73,6 +73,58 @@ static const gnutls_group_entry_st supported_groups[] = {
.tls_id = 29,
.pk = GNUTLS_PK_ECDH_X25519
},
+#ifdef ENABLE_GOST
+ /* draft-smyshlyaev-tls12-gost-suites-06, Section 6 */
+ {
+ .name = "GC256A",
+ .id = GNUTLS_GROUP_GC256A,
+ .curve = GNUTLS_ECC_CURVE_GOST256A,
+ .pk = GNUTLS_PK_GOST_12_256,
+ .tls_id = 34,
+ },
+ {
+ .name = "GC256B",
+ .id = GNUTLS_GROUP_GC256B,
+ .curve = GNUTLS_ECC_CURVE_GOST256B,
+ .pk = GNUTLS_PK_GOST_12_256,
+ .tls_id = 35,
+ },
+ {
+ .name = "GC256C",
+ .id = GNUTLS_GROUP_GC256C,
+ .curve = GNUTLS_ECC_CURVE_GOST256C,
+ .pk = GNUTLS_PK_GOST_12_256,
+ .tls_id = 36,
+ },
+ {
+ .name = "GC256D",
+ .id = GNUTLS_GROUP_GC256D,
+ .curve = GNUTLS_ECC_CURVE_GOST256D,
+ .pk = GNUTLS_PK_GOST_12_256,
+ .tls_id = 37,
+ },
+ {
+ .name = "GC512A",
+ .id = GNUTLS_GROUP_GC512A,
+ .curve = GNUTLS_ECC_CURVE_GOST512A,
+ .pk = GNUTLS_PK_GOST_12_512,
+ .tls_id = 38,
+ },
+ {
+ .name = "GC512B",
+ .id = GNUTLS_GROUP_GC512B,
+ .curve = GNUTLS_ECC_CURVE_GOST512B,
+ .pk = GNUTLS_PK_GOST_12_512,
+ .tls_id = 39,
+ },
+ {
+ .name = "GC512C",
+ .id = GNUTLS_GROUP_GC512C,
+ .curve = GNUTLS_ECC_CURVE_GOST512C,
+ .pk = GNUTLS_PK_GOST_12_512,
+ .tls_id = 40,
+ },
+#endif
#ifdef ENABLE_DHE
{
.name = "FFDHE2048",
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/crypto-backend.h b/lib/crypto-backend.h
index 33eca6031c..c083b16498 100644
--- a/lib/crypto-backend.h
+++ b/lib/crypto-backend.h
@@ -397,6 +397,7 @@ typedef struct gnutls_crypto_pk {
int (*derive) (gnutls_pk_algorithm_t, gnutls_datum_t * out,
const gnutls_pk_params_st * priv,
const gnutls_pk_params_st * pub,
+ const gnutls_datum_t *nonce,
unsigned int flags);
int (*curve_exists) (gnutls_ecc_curve_t); /* true/false */
@@ -433,4 +434,18 @@ _gnutls_prf_raw(gnutls_mac_algorithm_t mac,
size_t seed_size, const uint8_t *seed, size_t outsize,
char *out);
+int _gnutls_gost_key_wrap(gnutls_gost_paramset_t gost_params,
+ const gnutls_datum_t *kek,
+ const gnutls_datum_t *ukm,
+ const gnutls_datum_t *cek,
+ gnutls_datum_t *enc,
+ gnutls_datum_t *imit);
+
+int _gnutls_gost_key_unwrap(gnutls_gost_paramset_t gost_params,
+ const gnutls_datum_t *kek,
+ const gnutls_datum_t *ukm,
+ const gnutls_datum_t *enc,
+ const gnutls_datum_t *imit,
+ gnutls_datum_t *cek);
+
#endif /* GNUTLS_LIB_CRYPTO_BACKEND_H */
diff --git a/lib/gnutls.asn b/lib/gnutls.asn
index 3e6b67ea56..b3adae054d 100644
--- a/lib/gnutls.asn
+++ b/lib/gnutls.asn
@@ -144,4 +144,26 @@ IssuerSignTool ::= SEQUENCE {
cAToolCert UTF8String -- (SIZE (1..100))
}
+Gost28147-89-EncryptedKey ::= SEQUENCE {
+ encryptedKey OCTET STRING, -- (SIZE (32))
+ maskKey [0] IMPLICIT OCTET STRING OPTIONAL,
+ macKey OCTET STRING -- (SIZE (1..4))
+}
+
+SubjectPublicKeyInfo ::= SEQUENCE {
+ algorithm AlgorithmIdentifier,
+ subjectPublicKey BIT STRING
+}
+
+GostR3410-TransportParameters ::= SEQUENCE {
+ encryptionParamSet OBJECT IDENTIFIER,
+ ephemeralPublicKey [0] IMPLICIT SubjectPublicKeyInfo OPTIONAL,
+ ukm OCTET STRING
+}
+
+GostR3410-KeyTransport ::= SEQUENCE {
+ sessionEncryptedKey Gost28147-89-EncryptedKey,
+ transportParameters [0] IMPLICIT GostR3410-TransportParameters OPTIONAL
+}
+
END
diff --git a/lib/gnutls_asn1_tab.c b/lib/gnutls_asn1_tab.c
index 018db87394..f5c88e1abf 100644
--- a/lib/gnutls_asn1_tab.c
+++ b/lib/gnutls_asn1_tab.c
@@ -98,10 +98,27 @@ const asn1_static_node gnutls_asn1_tab[] = {
{ "encryptionParamSet", 16396, NULL },
{ "GOSTPrivateKey", 1073741831, NULL },
{ "GOSTPrivateKeyOld", 1073741827, NULL },
- { "IssuerSignTool", 536870917, NULL },
+ { "IssuerSignTool", 1610612741, NULL },
{ "signTool", 1073741858, NULL },
{ "cATool", 1073741858, NULL },
{ "signToolCert", 1073741858, NULL },
{ "cAToolCert", 34, NULL },
+ { "Gost28147-89-EncryptedKey", 1610612741, NULL },
+ { "encryptedKey", 1073741831, NULL },
+ { "maskKey", 1610637319, NULL },
+ { NULL, 4104, "0"},
+ { "macKey", 7, NULL },
+ { "SubjectPublicKeyInfo", 1610612741, NULL },
+ { "algorithm", 1073741826, "AlgorithmIdentifier"},
+ { "subjectPublicKey", 6, NULL },
+ { "GostR3410-TransportParameters", 1610612741, NULL },
+ { "encryptionParamSet", 1073741836, NULL },
+ { "ephemeralPublicKey", 1610637314, "SubjectPublicKeyInfo"},
+ { NULL, 4104, "0"},
+ { "ukm", 7, NULL },
+ { "GostR3410-KeyTransport", 536870917, NULL },
+ { "sessionEncryptedKey", 1073741826, "Gost28147-89-EncryptedKey"},
+ { "transportParameters", 536895490, "GostR3410-TransportParameters"},
+ { NULL, 4104, "0"},
{ NULL, 0, NULL }
};
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index f4bbbce306..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;
/**
@@ -1027,6 +1029,13 @@ typedef enum {
* @GNUTLS_GROUP_SECP384R1: the SECP384R1 curve group
* @GNUTLS_GROUP_SECP521R1: the SECP521R1 curve group
* @GNUTLS_GROUP_X25519: the X25519 curve group
+ * @GNUTLS_GROUP_GC256A: the GOST R 34.10 TC26 256 A curve group
+ * @GNUTLS_GROUP_GC256B: the GOST R 34.10 TC26 256 B curve group
+ * @GNUTLS_GROUP_GC256C: the GOST R 34.10 TC26 256 C curve group
+ * @GNUTLS_GROUP_GC256D: the GOST R 34.10 TC26 256 D curve group
+ * @GNUTLS_GROUP_GC512A: the GOST R 34.10 TC26 512 A curve group
+ * @GNUTLS_GROUP_GC512B: the GOST R 34.10 TC26 512 B curve group
+ * @GNUTLS_GROUP_GC512C: the GOST R 34.10 TC26 512 C curve group
* @GNUTLS_GROUP_FFDHE2048: the FFDHE2048 group
* @GNUTLS_GROUP_FFDHE3072: the FFDHE3072 group
* @GNUTLS_GROUP_FFDHE4096: the FFDHE4096 group
@@ -1046,6 +1055,14 @@ typedef enum {
GNUTLS_GROUP_SECP521R1 = GNUTLS_ECC_CURVE_SECP521R1,
GNUTLS_GROUP_X25519 = GNUTLS_ECC_CURVE_X25519,
+ GNUTLS_GROUP_GC256A = GNUTLS_ECC_CURVE_GOST256A,
+ GNUTLS_GROUP_GC256B = GNUTLS_ECC_CURVE_GOST256B,
+ GNUTLS_GROUP_GC256C = GNUTLS_ECC_CURVE_GOST256C,
+ GNUTLS_GROUP_GC256D = GNUTLS_ECC_CURVE_GOST256D,
+ GNUTLS_GROUP_GC512A = GNUTLS_ECC_CURVE_GOST512A,
+ GNUTLS_GROUP_GC512B = GNUTLS_ECC_CURVE_GOST512B,
+ GNUTLS_GROUP_GC512C = GNUTLS_ECC_CURVE_GOST512C,
+
GNUTLS_GROUP_FFDHE2048 = 256,
GNUTLS_GROUP_FFDHE3072,
GNUTLS_GROUP_FFDHE4096,
diff --git a/lib/nettle/Makefile.am b/lib/nettle/Makefile.am
index 035102f127..c1ac2b2125 100644
--- a/lib/nettle/Makefile.am
+++ b/lib/nettle/Makefile.am
@@ -74,7 +74,7 @@ endif
if ENABLE_GOST
libcrypto_la_SOURCES += \
gost/nettle-write.h \
- gost/gost28147.c gost/gost28147.h \
+ gost/gost28147.c gost/gost28147.h gost/gost-wrap.c \
gost/gosthash94.c gost/gosthash94.h gost/gosthash94-meta.c \
gost/streebog.c gost/streebog.h gost/streebog-meta.c \
gost/hmac-gosthash94.c gost/hmac-streebog.c gost/hmac-gost.h
@@ -92,6 +92,8 @@ libcrypto_la_SOURCES += \
gost/ecc-gost512a.c gost/ecc-gost512a-32.h gost/ecc-gost512a-64.h \
gost/ecc-internal.h gost/gmp-glue.h \
gost/ecc-gostdsa-sign.c gost/ecc-gostdsa-verify.c \
- gost/gostdsa-mask.c gost/gostdsa-sign.c gost/gostdsa-verify.c \
+ gost/gostdsa-mask.c gost/gostdsa-sign.c gost/gostdsa-verify.c gost/gostdsa-vko.c \
gost/gostdsa.h gost/ecc-gost-curve.h gost/ecc-gost-hash.c
+
+libcrypto_la_SOURCES += gost_keywrap.c
endif
diff --git a/lib/nettle/gost/gost-wrap.c b/lib/nettle/gost/gost-wrap.c
new file mode 100644
index 0000000000..63e1c321e2
--- /dev/null
+++ b/lib/nettle/gost/gost-wrap.c
@@ -0,0 +1,132 @@
+/* GOST 28147-89 (Magma) implementation
+ *
+ * Copyright: 2015, 2016 Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+ * Copyright: 2009-2012 Aleksey Kravchenko <rhash.admin@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gnutls_int.h>
+
+#include <string.h>
+
+#include <nettle/macros.h>
+#include "gost28147.h"
+#include <nettle/cfb.h>
+#include <nettle/memops.h>
+
+void
+gost28147_kdf_cryptopro(const struct gost28147_param *param,
+ const uint8_t *in,
+ const uint8_t *ukm,
+ uint8_t *out)
+{
+ struct gost28147_ctx ctx;
+ int i;
+
+ memcpy(out, in, GOST28147_KEY_SIZE);
+ for (i = 0; i < 8; i++) {
+ uint8_t mask;
+ uint8_t *p;
+ uint8_t iv[GOST28147_BLOCK_SIZE];
+ uint32_t block[2] = {0, 0};
+ uint32_t t;
+
+ for (p = out, mask = 1; mask; mask <<= 1) {
+ t = LE_READ_UINT32(p);
+ p += 4;
+ if (mask & ukm[i])
+ block[0] += t;
+ else
+ block[1] += t;
+ }
+
+ LE_WRITE_UINT32(iv + 0, block[0]);
+ LE_WRITE_UINT32(iv + 4, block[1]);
+
+ gost28147_set_key(&ctx, out);
+ gost28147_set_param(&ctx, param);
+ cfb_encrypt(&ctx,
+ (nettle_cipher_func*)gost28147_encrypt_for_cfb,
+ GOST28147_BLOCK_SIZE, iv,
+ GOST28147_KEY_SIZE, out, out);
+ }
+}
+
+void
+gost28147_key_wrap_cryptopro(const struct gost28147_param *param,
+ const uint8_t *kek,
+ const uint8_t *ukm, size_t ukm_size,
+ const uint8_t *cek,
+ uint8_t *enc,
+ uint8_t *imit)
+{
+ uint8_t kd[GOST28147_KEY_SIZE];
+ struct gost28147_ctx ctx;
+ struct gost28147_imit_ctx ictx;
+
+ assert(ukm_size >= GOST28147_IMIT_BLOCK_SIZE);
+
+ gost28147_kdf_cryptopro(param, kek, ukm, kd);
+ gost28147_set_key(&ctx, kd);
+ gost28147_set_param(&ctx, param);
+ gost28147_encrypt(&ctx, GOST28147_KEY_SIZE, enc, cek);
+
+ gost28147_imit_init(&ictx);
+ gost28147_imit_set_key(&ictx, GOST28147_KEY_SIZE, kd);
+ gost28147_imit_set_param(&ictx, param);
+ gost28147_imit_set_nonce(&ictx, ukm);
+ gost28147_imit_update(&ictx, GOST28147_KEY_SIZE, cek);
+ gost28147_imit_digest(&ictx, GOST28147_IMIT_DIGEST_SIZE, imit);
+}
+
+int
+gost28147_key_unwrap_cryptopro(const struct gost28147_param *param,
+ const uint8_t *kek,
+ const uint8_t *ukm, size_t ukm_size,
+ const uint8_t *enc,
+ const uint8_t *imit,
+ uint8_t *cek)
+{
+ uint8_t kd[GOST28147_KEY_SIZE];
+ uint8_t mac[GOST28147_IMIT_DIGEST_SIZE];
+ struct gost28147_ctx ctx;
+ struct gost28147_imit_ctx ictx;
+
+ assert(ukm_size >= GOST28147_IMIT_BLOCK_SIZE);
+
+ gost28147_kdf_cryptopro(param, kek, ukm, kd);
+ gost28147_set_key(&ctx, kd);
+ gost28147_set_param(&ctx, param);
+ gost28147_decrypt(&ctx, GOST28147_KEY_SIZE, cek, enc);
+
+ gost28147_imit_init(&ictx);
+ gost28147_imit_set_key(&ictx, GOST28147_KEY_SIZE, kd);
+ gost28147_imit_set_param(&ictx, param);
+ gost28147_imit_set_nonce(&ictx, ukm);
+ gost28147_imit_update(&ictx, GOST28147_KEY_SIZE, cek);
+ gost28147_imit_digest(&ictx, GOST28147_IMIT_DIGEST_SIZE, mac);
+
+ return memeql_sec(mac, imit, GOST28147_IMIT_DIGEST_SIZE);
+}
diff --git a/lib/nettle/gost/gost28147.h b/lib/nettle/gost/gost28147.h
index 5fbab90ef1..ae4a385589 100644
--- a/lib/nettle/gost/gost28147.h
+++ b/lib/nettle/gost/gost28147.h
@@ -69,6 +69,10 @@ extern "C" {
#define gost28147_cnt_set_iv _gnutls_gost28147_cnt_set_iv
#define gost28147_cnt_crypt _gnutls_gost28147_cnt_crypt
+#define gost28147_kdf_cryptopro _gnutls_gost28147_kdf_cryptopro
+#define gost28147_key_wrap_cryptopro _gnutls_gost28147_key_wrap_cryptopro
+#define gost28147_key_unwrap_cryptopro _gnutls_gost28147_key_unwrap_cryptopro
+
#define gost28147_imit_init _gnutls_gost28147_imit_init
#define gost28147_imit_set_key _gnutls_gost28147_imit_set_key
#define gost28147_imit_set_nonce _gnutls_gost28147_imit_set_nonce
@@ -147,6 +151,27 @@ gost28147_cnt_crypt(struct gost28147_cnt_ctx *ctx,
size_t length, uint8_t *dst,
const uint8_t *src);
+void
+gost28147_kdf_cryptopro(const struct gost28147_param *param,
+ const uint8_t *in,
+ const uint8_t *ukm,
+ uint8_t *out);
+void
+gost28147_key_wrap_cryptopro(const struct gost28147_param *param,
+ const uint8_t *kek,
+ const uint8_t *ukm, size_t ukm_size,
+ const uint8_t *cek,
+ uint8_t *enc,
+ uint8_t *imit);
+
+int
+gost28147_key_unwrap_cryptopro(const struct gost28147_param *param,
+ const uint8_t *kek,
+ const uint8_t *ukm, size_t ukm_size,
+ const uint8_t *enc,
+ const uint8_t *imit,
+ uint8_t *cek);
+
#define GOST28147_IMIT_DIGEST_SIZE 4
#define GOST28147_IMIT_BLOCK_SIZE GOST28147_BLOCK_SIZE
#define GOST28147_IMIT_KEY_SIZE GOST28147_KEY_SIZE
diff --git a/lib/nettle/gost/gostdsa-vko.c b/lib/nettle/gost/gostdsa-vko.c
new file mode 100644
index 0000000000..89dff1cc45
--- /dev/null
+++ b/lib/nettle/gost/gostdsa-vko.c
@@ -0,0 +1,78 @@
+/* gostdsa-vko.c
+
+ Copyright (C) 2016 Dmitry Eremin-Solenikov
+
+ This file is part of GNU Nettle.
+
+ GNU Nettle is free software: you can redistribute it and/or
+ modify it under the terms of either:
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ or both in parallel, as here.
+
+ GNU Nettle 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
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see http://www.gnu.org/licenses/.
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gnutls_int.h>
+
+#include <stdlib.h>
+
+#include "ecc-internal.h"
+#include "gostdsa.h"
+
+int
+gostdsa_vko(const struct ecc_scalar *key,
+ const struct ecc_point *pub,
+ size_t ukm_length, const uint8_t *ukm,
+ size_t out_length, uint8_t *out)
+{
+ const struct ecc_curve *ecc = key->ecc;
+ unsigned bsize = (ecc_bit_size(ecc) + 7) / 8;
+ mp_size_t size = ecc->p.size;
+ mp_size_t itch = 4*size + ecc->mul_itch;
+ mp_limb_t *scratch;
+
+ if (itch < 5*size + ecc->h_to_a_itch)
+ itch = 5*size + ecc->h_to_a_itch;
+
+ if (pub->ecc != ecc)
+ return 0;
+
+ if (out_length < 2 * bsize) {
+ return 0;
+ }
+
+ scratch = gmp_alloc_limbs (itch);
+
+ mpn_set_base256_le (scratch, size, ukm, ukm_length);
+ if (mpn_zero_p (scratch, size))
+ mpn_add_1 (scratch, scratch, size, 1);
+ ecc_modq_mul (ecc, scratch + 3*size, key->p, scratch);
+ ecc->mul (ecc, scratch, scratch + 3*size, pub->p, scratch + 4*size);
+ ecc->h_to_a (ecc, 0, scratch + 3*size, scratch, scratch + 5*size);
+ mpn_get_base256_le (out, bsize, scratch + 3*size, size);
+ mpn_get_base256_le (out+bsize, bsize, scratch + 4*size, size);
+ gmp_free_limbs (scratch, itch);
+
+ return 2 * bsize;
+}
diff --git a/lib/nettle/gost/gostdsa.h b/lib/nettle/gost/gostdsa.h
index 9b0f517529..9e0375f2ce 100644
--- a/lib/nettle/gost/gostdsa.h
+++ b/lib/nettle/gost/gostdsa.h
@@ -47,6 +47,7 @@ extern "C" {
#define gostdsa_sign _gnutls_gostdsa_sign
#define gostdsa_verify _gnutls_gostdsa_verify
#define gostdsa_unmask_key _gnutls_gostdsa_unmask_key
+#define gostdsa_vko _gnutls_gostdsa_vko
#define ecc_gostdsa_sign _gnutls_ecc_gostdsa_sign
#define ecc_gostdsa_sign_itch _gnutls_ecc_gostdsa_sign_itch
#define ecc_gostdsa_verify _gnutls_ecc_gostdsa_verify
@@ -75,6 +76,12 @@ int
gostdsa_unmask_key (const struct ecc_curve *ecc,
mpz_t key);
+int
+gostdsa_vko(const struct ecc_scalar *key,
+ const struct ecc_point *pub,
+ size_t ukm_length, const uint8_t *ukm,
+ size_t out_length, uint8_t *out);
+
/* Low-level GOSTDSA functions. */
mp_size_t
ecc_gostdsa_sign_itch (const struct ecc_curve *ecc);
diff --git a/lib/nettle/gost_keywrap.c b/lib/nettle/gost_keywrap.c
new file mode 100644
index 0000000000..ca186702df
--- /dev/null
+++ b/lib/nettle/gost_keywrap.c
@@ -0,0 +1,121 @@
+/*
+ * 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 "gost/gost28147.h"
+
+static const struct gost28147_param *
+_gnutls_gost_get_param(gnutls_gost_paramset_t param)
+{
+ if (param == GNUTLS_GOST_PARAMSET_TC26_Z)
+ return &gost28147_param_TC26_Z;
+ else if (param == GNUTLS_GOST_PARAMSET_CP_A)
+ return &gost28147_param_CryptoPro_A;
+ else if (param == GNUTLS_GOST_PARAMSET_CP_B)
+ return &gost28147_param_CryptoPro_B;
+ else if (param == GNUTLS_GOST_PARAMSET_CP_C)
+ return &gost28147_param_CryptoPro_C;
+ else if (param == GNUTLS_GOST_PARAMSET_CP_D)
+ return &gost28147_param_CryptoPro_D;
+
+ gnutls_assert();
+
+ return NULL;
+}
+
+int _gnutls_gost_key_wrap(gnutls_gost_paramset_t gost_params,
+ const gnutls_datum_t *kek,
+ const gnutls_datum_t *ukm,
+ const gnutls_datum_t *cek,
+ gnutls_datum_t *enc,
+ gnutls_datum_t *imit)
+{
+ const struct gost28147_param *gp;
+
+ gp = _gnutls_gost_get_param(gost_params);
+ if (gp == NULL) {
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ }
+
+ if (kek->size != GOST28147_KEY_SIZE ||
+ cek->size != GOST28147_KEY_SIZE ||
+ ukm->size < GOST28147_IMIT_BLOCK_SIZE) {
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ }
+
+ enc->size = GOST28147_KEY_SIZE;
+ enc->data = gnutls_malloc(enc->size);
+ if (enc->data == NULL) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ imit->size = GOST28147_IMIT_DIGEST_SIZE;
+ imit->data = gnutls_malloc(imit->size);
+ if (imit->data == NULL) {
+ _gnutls_free_datum(enc);
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ gost28147_key_wrap_cryptopro(gp, kek->data, ukm->data, ukm->size,
+ cek->data, enc->data, imit->data);
+
+ return 0;
+}
+
+int _gnutls_gost_key_unwrap(gnutls_gost_paramset_t gost_params,
+ const gnutls_datum_t *kek,
+ const gnutls_datum_t *ukm,
+ const gnutls_datum_t *enc,
+ const gnutls_datum_t *imit,
+ gnutls_datum_t *cek)
+{
+ const struct gost28147_param *gp;
+ int ret;
+
+ gp = _gnutls_gost_get_param(gost_params);
+ if (gp == NULL) {
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ }
+
+ if (kek->size != GOST28147_KEY_SIZE ||
+ enc->size != GOST28147_KEY_SIZE ||
+ imit->size != GOST28147_IMIT_DIGEST_SIZE ||
+ ukm->size < GOST28147_IMIT_BLOCK_SIZE) {
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ }
+
+ cek->size = GOST28147_KEY_SIZE;
+ cek->data = gnutls_malloc(cek->size);
+ if (cek->data == NULL) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ ret = gost28147_key_unwrap_cryptopro(gp, kek->data,
+ ukm->data, ukm->size,
+ enc->data, imit->data,
+ cek->data);
+ if (ret == 0) {
+ gnutls_assert();
+ _gnutls_free_temp_key_datum(cek);
+ return GNUTLS_E_DECRYPTION_FAILED;
+ }
+
+ return 0;
+}
diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c
index b6bb735566..42d540cb46 100644
--- a/lib/nettle/pk.c
+++ b/lib/nettle/pk.c
@@ -242,6 +242,7 @@ static int _wrap_nettle_pk_derive(gnutls_pk_algorithm_t algo,
gnutls_datum_t * out,
const gnutls_pk_params_st * priv,
const gnutls_pk_params_st * pub,
+ const gnutls_datum_t * nonce,
unsigned int flags)
{
int ret;
@@ -252,6 +253,9 @@ static int _wrap_nettle_pk_derive(gnutls_pk_algorithm_t algo,
bigint_t k = NULL, ff = NULL, r = NULL;
unsigned int bits;
+ if (nonce != NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
f = pub->params[DH_Y];
x = priv->params[DH_X];
q = priv->params[DH_Q];
@@ -343,6 +347,9 @@ dh_cleanup:
out->data = NULL;
+ if (nonce != NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
curve = get_supported_nist_curve(priv->curve);
if (curve == NULL)
return
@@ -384,6 +391,9 @@ dh_cleanup:
{
unsigned size = gnutls_ecc_curve_get_size(priv->curve);
+ if (nonce != NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
/* The point is in pub, while the private part (scalar) in priv. */
if (size == 0 || priv->raw_priv.size != size)
@@ -407,6 +417,57 @@ dh_cleanup:
}
break;
}
+#if ENABLE_GOST
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ {
+ struct ecc_scalar ecc_priv;
+ struct ecc_point ecc_pub;
+ const struct ecc_curve *curve;
+
+ out->data = NULL;
+
+ curve = get_supported_gost_curve(priv->curve);
+ if (curve == NULL)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+
+ if (nonce == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ ret = _gost_params_to_pubkey(pub, &ecc_pub, curve);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gost_params_to_privkey(priv, &ecc_priv, curve);
+ if (ret < 0) {
+ ecc_point_clear(&ecc_pub);
+ return gnutls_assert_val(ret);
+ }
+
+ out->size = 2 * gnutls_ecc_curve_get_size(priv->curve);
+ out->data = gnutls_malloc(out->size);
+ if (out->data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto gost_cleanup;
+ }
+
+ out->size = gostdsa_vko(&ecc_priv, &ecc_pub,
+ nonce->size, nonce->data,
+ out->size, out->data);
+ if (out->size == 0)
+ ret = GNUTLS_E_INVALID_REQUEST;
+
+ gost_cleanup:
+ ecc_point_clear(&ecc_pub);
+ ecc_scalar_zclear(&ecc_priv);
+ if (ret < 0)
+ goto cleanup;
+ break;
+ }
+#endif
default:
gnutls_assert();
ret = GNUTLS_E_INTERNAL_ERROR;
diff --git a/lib/pk.h b/lib/pk.h
index 6c83cfa295..cc61e08cef 100644
--- a/lib/pk.h
+++ b/lib/pk.h
@@ -33,8 +33,9 @@ extern gnutls_crypto_pk_st _gnutls_pk_ops;
#define _gnutls_pk_verify( algo, data, sig, params, sign_params) _gnutls_pk_ops.verify( algo, data, sig, params, sign_params)
#define _gnutls_pk_verify_priv_params( algo, params) _gnutls_pk_ops.verify_priv_params( algo, params)
#define _gnutls_pk_verify_pub_params( algo, params) _gnutls_pk_ops.verify_pub_params( algo, params)
-#define _gnutls_pk_derive( algo, out, pub, priv) _gnutls_pk_ops.derive( algo, out, pub, priv, 0)
-#define _gnutls_pk_derive_tls13( algo, out, pub, priv) _gnutls_pk_ops.derive( algo, out, pub, priv, PK_DERIVE_TLS13)
+#define _gnutls_pk_derive( algo, out, pub, priv) _gnutls_pk_ops.derive( algo, out, pub, priv, NULL, 0)
+#define _gnutls_pk_derive_nonce( algo, out, pub, priv, nonce) _gnutls_pk_ops.derive( algo, out, pub, priv, nonce, 0)
+#define _gnutls_pk_derive_tls13( algo, out, pub, priv) _gnutls_pk_ops.derive( algo, out, pub, priv, NULL, PK_DERIVE_TLS13)
#define _gnutls_pk_generate_keys( algo, bits, params, temporal) _gnutls_pk_ops.generate_keys( algo, bits, params, temporal)
#define _gnutls_pk_generate_params( algo, bits, priv) _gnutls_pk_ops.generate_params( algo, bits, priv)
#define _gnutls_pk_hash_algorithm( pk, sig, params, hash) _gnutls_pk_ops.hash_algorithm(pk, sig, params, hash)
diff --git a/lib/vko.c b/lib/vko.c
new file mode 100644
index 0000000000..a419390ffa
--- /dev/null
+++ b/lib/vko.c
@@ -0,0 +1,299 @@
+/*
+ * 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_TYPE 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_TYPE 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);
+ goto cleanup;
+ }
+
+ 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;
+}
diff --git a/lib/vko.h b/lib/vko.h
new file mode 100644
index 0000000000..d4ff3891d9
--- /dev/null
+++ b/lib/vko.h
@@ -0,0 +1,38 @@
+/*
+ * 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/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_VKO_H
+#define GNUTLS_LIB_VKO_H
+
+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
+_gnutls_gost_keytrans_decrypt(gnutls_pk_params_st *priv,
+ gnutls_datum_t *cek,
+ gnutls_datum_t *ukm,
+ gnutls_datum_t *out);
+
+#endif /* GNUTLS_LIB_VKO_H */