summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDmitry Eremin-Solenikov <dbaryshkov@gmail.com>2016-12-02 06:26:55 +0300
committerDmitry Eremin-Solenikov <dbaryshkov@gmail.com>2019-11-07 18:41:28 +0300
commit7f93e7f5c649d50e15f8ac3d253cb5926e5757d6 (patch)
tree6d3e1d3269294645315eb31474ef78689f3de518 /lib
parentcdc4fc288d87f91f974aa23b6e8595a53970ce00 (diff)
downloadgnutls-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.am8
-rw-r--r--lib/crypto-backend.h14
-rw-r--r--lib/gnutls.asn22
-rw-r--r--lib/gnutls_asn1_tab.c19
-rw-r--r--lib/nettle/Makefile.am2
-rw-r--r--lib/nettle/gost_keywrap.c121
-rw-r--r--lib/vko.c299
-rw-r--r--lib/vko.h38
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 */