diff options
author | Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | 2016-12-02 06:26:55 +0300 |
---|---|---|
committer | Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | 2019-11-07 18:41:28 +0300 |
commit | 7f93e7f5c649d50e15f8ac3d253cb5926e5757d6 (patch) | |
tree | 6d3e1d3269294645315eb31474ef78689f3de518 /lib | |
parent | cdc4fc288d87f91f974aa23b6e8595a53970ce00 (diff) | |
download | gnutls-7f93e7f5c649d50e15f8ac3d253cb5926e5757d6.tar.gz |
Add GOST key transport support
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 8 | ||||
-rw-r--r-- | lib/crypto-backend.h | 14 | ||||
-rw-r--r-- | lib/gnutls.asn | 22 | ||||
-rw-r--r-- | lib/gnutls_asn1_tab.c | 19 | ||||
-rw-r--r-- | lib/nettle/Makefile.am | 2 | ||||
-rw-r--r-- | lib/nettle/gost_keywrap.c | 121 | ||||
-rw-r--r-- | lib/vko.c | 299 | ||||
-rw-r--r-- | lib/vko.h | 38 |
8 files changed, 522 insertions, 1 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/crypto-backend.h b/lib/crypto-backend.h index a3c52d4da8..c083b16498 100644 --- a/lib/crypto-backend.h +++ b/lib/crypto-backend.h @@ -434,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/nettle/Makefile.am b/lib/nettle/Makefile.am index 7260e39bce..c1ac2b2125 100644 --- a/lib/nettle/Makefile.am +++ b/lib/nettle/Makefile.am @@ -94,4 +94,6 @@ libcrypto_la_SOURCES += \ gost/ecc-gostdsa-sign.c gost/ecc-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_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/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 */ |