diff options
author | Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | 2019-11-08 23:03:16 +0000 |
---|---|---|
committer | Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | 2019-11-08 23:03:16 +0000 |
commit | 3d4928e4268bea40a37e796795e42353d4415a6d (patch) | |
tree | 09c47bc9a321b97e42be721cf95951fb909b4227 | |
parent | b5029c0a57ba881e1db4a8c7d5c680f0f2df8dc4 (diff) | |
parent | 6eadc827ea6323cf7d752cba1b41cf9b93aae9b9 (diff) | |
download | gnutls-3d4928e4268bea40a37e796795e42353d4415a6d.tar.gz |
Merge branch 'gost-split-2' into 'master'
GOST key exchange support
See merge request gnutls/gnutls!1097
-rw-r--r-- | devel/libgnutls-latest-x86_64.abi | 8 | ||||
-rw-r--r-- | lib/Makefile.am | 8 | ||||
-rw-r--r-- | lib/algorithms.h | 2 | ||||
-rw-r--r-- | lib/algorithms/ecc.c | 39 | ||||
-rw-r--r-- | lib/algorithms/groups.c | 52 | ||||
-rw-r--r-- | lib/algorithms/kx.c | 5 | ||||
-rw-r--r-- | lib/algorithms/publickey.c | 3 | ||||
-rw-r--r-- | lib/auth/Makefile.am | 2 | ||||
-rw-r--r-- | lib/auth/vko_gost.c | 323 | ||||
-rw-r--r-- | lib/crypto-backend.h | 15 | ||||
-rw-r--r-- | lib/gnutls.asn | 22 | ||||
-rw-r--r-- | lib/gnutls_asn1_tab.c | 19 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 19 | ||||
-rw-r--r-- | lib/nettle/Makefile.am | 6 | ||||
-rw-r--r-- | lib/nettle/gost/gost-wrap.c | 132 | ||||
-rw-r--r-- | lib/nettle/gost/gost28147.h | 25 | ||||
-rw-r--r-- | lib/nettle/gost/gostdsa-vko.c | 78 | ||||
-rw-r--r-- | lib/nettle/gost/gostdsa.h | 7 | ||||
-rw-r--r-- | lib/nettle/gost_keywrap.c | 121 | ||||
-rw-r--r-- | lib/nettle/pk.c | 61 | ||||
-rw-r--r-- | lib/pk.h | 5 | ||||
-rw-r--r-- | lib/vko.c | 299 | ||||
-rw-r--r-- | lib/vko.h | 38 |
23 files changed, 1282 insertions, 7 deletions
diff --git a/devel/libgnutls-latest-x86_64.abi b/devel/libgnutls-latest-x86_64.abi index b2c058d71d..cd7467d067 100644 --- a/devel/libgnutls-latest-x86_64.abi +++ b/devel/libgnutls-latest-x86_64.abi @@ -1522,6 +1522,7 @@ <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'> @@ -1670,6 +1671,13 @@ <enumerator name='GNUTLS_GROUP_SECP384R1' value='3'/> <enumerator name='GNUTLS_GROUP_SECP521R1' value='4'/> <enumerator name='GNUTLS_GROUP_X25519' value='6'/> + <enumerator name='GNUTLS_GROUP_GC256A' value='16'/> + <enumerator name='GNUTLS_GROUP_GC256B' value='17'/> + <enumerator name='GNUTLS_GROUP_GC256C' value='18'/> + <enumerator name='GNUTLS_GROUP_GC256D' value='19'/> + <enumerator name='GNUTLS_GROUP_GC512A' value='13'/> + <enumerator name='GNUTLS_GROUP_GC512B' value='14'/> + <enumerator name='GNUTLS_GROUP_GC512C' value='15'/> <enumerator name='GNUTLS_GROUP_FFDHE2048' value='256'/> <enumerator name='GNUTLS_GROUP_FFDHE3072' value='257'/> <enumerator name='GNUTLS_GROUP_FFDHE4096' value='258'/> 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; @@ -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 */ |