]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Add GOST key transport support
authorDmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Fri, 2 Dec 2016 03:26:55 +0000 (06:26 +0300)
committerDmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Thu, 7 Nov 2019 15:41:28 +0000 (18:41 +0300)
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
lib/Makefile.am
lib/crypto-backend.h
lib/gnutls.asn
lib/gnutls_asn1_tab.c
lib/nettle/Makefile.am
lib/nettle/gost_keywrap.c [new file with mode: 0644]
lib/vko.c [new file with mode: 0644]
lib/vko.h [new file with mode: 0644]

index f1e3bb90b629d4d03abe57f1750534c66221190c..eddd1167a72c8c5867ba205d1b0324a5ce0140d4 100644 (file)
@@ -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
index a3c52d4da8fecce3ef07a415ad7558279425907d..c083b164983ddbccdbc1a1edc5e4721f9a1472a0 100644 (file)
@@ -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 */
index 3e6b67ea568282d043a044316d64f3122fe78d0f..b3adae054d9e78fd14d364ec71d1fa97eb3d8ba8 100644 (file)
@@ -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
index 018db87394d1ece7b679b664bf6cfdd9a8794c60..f5c88e1abf7a7a7f1f9b0a6781b167f79d972b08 100644 (file)
@@ -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 }
 };
index 7260e39bce62bf858f98fff27a85bb9b4d427de7..c1ac2b21258bd8dc8e1f1048259ca43506fcb9a7 100644 (file)
@@ -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 (file)
index 0000000..ca18670
--- /dev/null
@@ -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 (file)
index 0000000..a419390
--- /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 (file)
index 0000000..d4ff389
--- /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 */