]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
pk: add flags to force RSA-PSS salt length to match digest length
authorDaiki Ueno <ueno@gnu.org>
Mon, 2 Aug 2021 16:32:28 +0000 (18:32 +0200)
committerDaiki Ueno <ueno@gnu.org>
Sat, 7 Aug 2021 06:51:52 +0000 (08:51 +0200)
This adds a couple of flags to RSA-PSS signing and verification, to
enforce that the salt length matches the digest length.  That is not
only recommended in RFC 4055, but also mandated in RFC 8446 in the TLS
1.3 context.

Signed-off-by: Daiki Ueno <ueno@gnu.org>
NEWS
lib/crypto-backend.h
lib/includes/gnutls/abstract.h
lib/includes/gnutls/x509.h
lib/nettle/pk.c
lib/privkey.c
lib/pubkey.c
lib/tls13-sig.c
tests/rsa-rsa-pss.c

diff --git a/NEWS b/NEWS
index b13c97b35ad36bde6cacc61ae890ff33f53be1db..0fcd043aa3d2868630448367f71cea3f6ecc40a3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,12 @@ Copyright (C) 2000-2016 Free Software Foundation, Inc.
 Copyright (C) 2013-2019 Nikos Mavrogiannopoulos
 See the end for copying conditions.
 
+* Version 3.7.3 (unreleased)
+
+** API and ABI modifications:
+GNUTLS_PRIVKEY_FLAG_RSA_PSS_FIXED_SALT_LENGTH: new flag in gnutls_privkey_flags_t
+GNUTLS_VERIFY_RSA_PSS_FIXED_SALT_LENGTH: new flag in gnutls_certificate_verify_flags
+
 * Version 3.7.2 (released 2021-05-29)
 
 ** libgnutls: The priority string option %DISABLE_TLS13_COMPAT_MODE was added
index 33005e73dd65287595ac0da47572fe8b6890e470..6cc1853cbefc74e83ce7a2de2e5e2aa40845178c 100644 (file)
@@ -243,7 +243,8 @@ typedef struct {
 typedef enum {
        GNUTLS_PK_FLAG_NONE = 0,
        GNUTLS_PK_FLAG_PROVABLE = 1,
-       GNUTLS_PK_FLAG_REPRODUCIBLE = 2
+       GNUTLS_PK_FLAG_REPRODUCIBLE = 2,
+       GNUTLS_PK_FLAG_RSA_PSS_FIXED_SALT_LENGTH = 4
 } gnutls_pk_flag_t;
 
 #define FIX_SIGN_PARAMS(params, flags, dig) do {               \
index 7e98ea2b1314a12cc81bd04503bb9a71a07a8843..c9f8067c1310b79754f43fb4132cb321c761bd76 100644 (file)
@@ -388,6 +388,9 @@ int gnutls_privkey_status(gnutls_privkey_t key);
  * @GNUTLS_PRIVKEY_FLAG_EXPORT_COMPAT: Keys generated or imported as provable require an extended format which cannot be read by previous versions
  *   of gnutls or other applications. By setting this flag the key will be exported in a backwards compatible way,
  *   even if the information about the seed used will be lost.
+ * @GNUTLS_PRIVKEY_FLAG_RSA_PSS_FIXED_SALT_LENGTH: When making an RSA-PSS
+ *   signature, use the salt whose length is equal to the digest length, as
+ *   mandated in RFC 8446 4.2.3.
  *
  * Enumeration of different certificate import flags.
  */
@@ -400,7 +403,8 @@ typedef enum gnutls_privkey_flags {
        GNUTLS_PRIVKEY_FLAG_EXPORT_COMPAT = 1 << 6,
        GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS = 1 << 7,
        GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE = 1 << 8,
-       GNUTLS_PRIVKEY_FLAG_CA = 1 << 9
+       GNUTLS_PRIVKEY_FLAG_CA = 1 << 9,
+       GNUTLS_PRIVKEY_FLAG_RSA_PSS_FIXED_SALT_LENGTH = 1 << 10
 } gnutls_privkey_flags_t;
 
 int gnutls_privkey_import_pkcs11(gnutls_privkey_t pkey,
index 5f54e888bc01044451301af7878ea8aa6882eec5..7953a30460401f113f58567541509bb976c59413 100644 (file)
@@ -959,6 +959,9 @@ int gnutls_x509_crl_set_number(gnutls_x509_crl_t crl,
  *   as in the TLS 1.0 protocol. Not all functions accept this flag.
  * @GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS: This signals the verification
  *   process, not to fail on unknown critical extensions.
+ * @GNUTLS_VERIFY_RSA_PSS_FIXED_SALT_LENGTH: Disallow RSA-PSS signatures made
+ *   with mismatching salt length with digest length, as mandated in RFC 8446
+ *   4.2.3.
  *
  * Enumeration of different certificate verify flags. Additional
  * verification profiles can be set using GNUTLS_PROFILE_TO_VFLAGS()
@@ -980,7 +983,8 @@ typedef enum gnutls_certificate_verify_flags {
        GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS = 1 << 12,
        GNUTLS_VERIFY_USE_TLS1_RSA = 1 << 13,
        GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS = 1 << 14,
-       GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1 = 1 << 15
+       GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1 = 1 << 15,
+       GNUTLS_VERIFY_RSA_PSS_FIXED_SALT_LENGTH = 1 << 16
        /* cannot exceed 2^24 due to GNUTLS_PROFILE_TO_VFLAGS() */
 } gnutls_certificate_verify_flags;
 
index ff8e3d15b45c673571be7d84f522129a73b928cd..16d9b4a04c5a92c7818533eb3ddc7b2590d185f9 100644 (file)
@@ -1491,6 +1491,13 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo,
                {
                        struct rsa_public_key pub;
 
+                       if ((sign_params->flags &
+                            GNUTLS_PK_FLAG_RSA_PSS_FIXED_SALT_LENGTH) &&
+                           sign_params->salt_size != vdata->size) {
+                               return gnutls_assert_val
+                                       (GNUTLS_E_PK_SIG_VERIFY_FAILED);
+                       }
+
                        ret = _rsa_params_to_pubkey(pk_params, &pub);
                        if (ret < 0)
                                return
index 9f02c5b062c4e8d0deb8ff67c6c1d5038f04ff63..0e8d29561f7cb3d48bacb0a8c3f13e449125e137 100644 (file)
@@ -373,6 +373,10 @@ _gnutls_privkey_update_spki_params(gnutls_privkey_t key,
                        ret = _gnutls_find_rsa_pss_salt_size(bits, me, salt_size);
                        if (ret < 0)
                                return gnutls_assert_val(ret);
+                       if (flags & GNUTLS_PRIVKEY_FLAG_RSA_PSS_FIXED_SALT_LENGTH &&
+                           (size_t)ret != _gnutls_hash_get_algo_len(me)) {
+                               return gnutls_assert_val(GNUTLS_E_CONSTRAINT_ERROR);
+                       }
                        params->salt_size = ret;
                }
                params->rsa_pss_dig = dig;
index 5fe30f798cc183a44fd41b39a567f82716b14ce9..a1735cf7667c966515b2d25e5105aa2a4132077f 100644 (file)
@@ -1931,6 +1931,9 @@ gnutls_pubkey_verify_data2(gnutls_pubkey_t pubkey,
                return gnutls_assert_val(ret);
 
        params.pk = se->pk;
+       if (flags & GNUTLS_VERIFY_RSA_PSS_FIXED_SALT_LENGTH) {
+               params.flags |= GNUTLS_PK_FLAG_RSA_PSS_FIXED_SALT_LENGTH;
+       }
 
        me = hash_to_entry(se->hash);
        if (me == NULL && !_gnutls_pk_is_not_prehashed(se->pk))
index a52295facae87e3ad06e43fea705bc3f6fe24a9f..3b193d71e795919e7f1bf8426ce995110930ee3d 100644 (file)
@@ -122,6 +122,7 @@ _gnutls13_handshake_verify_data(gnutls_session_t session,
        p.data = buf.data;
        p.size = buf.length;
 
+       verify_flags |= GNUTLS_VERIFY_RSA_PSS_FIXED_SALT_LENGTH;
        ret = gnutls_pubkey_verify_data2(cert->pubkey, se->id, verify_flags, &p,
                                         signature);
        if (ret < 0) {
@@ -200,7 +201,9 @@ _gnutls13_handshake_sign_data(gnutls_session_t session,
        p.data = buf.data;
        p.size = buf.length;
 
-       ret = gnutls_privkey_sign_data2(pkey, se->id, 0, &p, signature);
+       ret = gnutls_privkey_sign_data2(pkey, se->id,
+                                       GNUTLS_PRIVKEY_FLAG_RSA_PSS_FIXED_SALT_LENGTH,
+                                       &p, signature);
        if (ret < 0) {
                gnutls_assert();
                goto cleanup;
index 7ba66803d575c7f31622674b8058ac7abce686e8..19a175b722cb98a43cf02f2d68150246cca76618 100644 (file)
@@ -84,85 +84,107 @@ static void inv_encryption_check(gnutls_pk_algorithm_t algorithm,
 
 }
 
-static void sign_verify_data(unsigned sigalgo, gnutls_privkey_t privkey)
+static void sign_verify_data(unsigned sigalgo, gnutls_privkey_t privkey,
+                            unsigned int sign_flags, unsigned int verify_flags,
+                            int sign_exp_error, int verify_exp_error)
 {
        int ret;
-       gnutls_pubkey_t pubkey;
-       gnutls_datum_t signature;
+       gnutls_datum_t signature = { NULL, 0 };
 
-       ret = gnutls_privkey_sign_data2(privkey, sigalgo, 0,
+       ret = gnutls_privkey_sign_data2(privkey, sigalgo, sign_flags,
                                        &raw_data, &signature);
-       if (ret < 0)
-               fail("gnutls_x509_privkey_sign_data\n");
+       if (ret != sign_exp_error)
+               fail("gnutls_x509_privkey_sign_data returned unexpected error: %s\n",
+                    gnutls_strerror(ret));
 
-       /* verify data */
-       assert(gnutls_pubkey_init(&pubkey) >= 0);
+       if (ret < 0) {
+               success("skipping verification as signing is expected to fail\n");
+       } else {
+               gnutls_pubkey_t pubkey;
 
-       ret = gnutls_pubkey_import_privkey(pubkey, privkey, 0, 0);
-       if (ret < 0)
-               fail("gnutls_pubkey_import_privkey\n");
+               /* verify data */
+               assert(gnutls_pubkey_init(&pubkey) >= 0);
 
-       ret = gnutls_pubkey_verify_data2(pubkey, sigalgo,
-                               0, &raw_data, &signature);
-       if (ret < 0)
-               fail("gnutls_pubkey_verify_data2\n");
+               ret = gnutls_pubkey_import_privkey(pubkey, privkey, 0, 0);
+               if (ret < 0)
+                       fail("gnutls_pubkey_import_privkey\n");
+
+               ret = gnutls_pubkey_verify_data2(pubkey, sigalgo,
+                                                verify_flags, &raw_data, &signature);
+               if (ret != verify_exp_error)
+                       fail("gnutls_pubkey_verify_data2 returned unexpected error: %s\n",
+                            gnutls_strerror(ret));
+
+               gnutls_pubkey_deinit(pubkey);
+       }
 
-       gnutls_pubkey_deinit(pubkey);
        gnutls_free(signature.data);
 }
 
-void doit(void)
+static void
+prepare_keys(gnutls_privkey_t *pkey_rsa_pss, gnutls_privkey_t *pkey_rsa,
+            gnutls_digest_algorithm_t dig, size_t salt_size)
 {
-       gnutls_privkey_t pkey_rsa_pss;
-       gnutls_privkey_t pkey_rsa;
+       gnutls_privkey_t pkey;
        gnutls_x509_privkey_t tkey;
        int ret;
        gnutls_x509_spki_t spki;
        gnutls_datum_t tmp;
 
-       ret = global_init();
-       if (ret < 0)
-               fail("global_init: %d\n", ret);
-
-       gnutls_global_set_log_function(tls_log_func);
-       if (debug)
-               gnutls_global_set_log_level(4711);
-
        assert(gnutls_x509_spki_init(&spki)>=0);
 
-       assert(gnutls_privkey_init(&pkey_rsa) >=0);
+       assert(gnutls_privkey_init(&pkey) >=0);
 
-       gnutls_x509_spki_set_rsa_pss_params(spki, GNUTLS_DIG_SHA256, 32);
+       gnutls_x509_spki_set_rsa_pss_params(spki, dig, salt_size);
 
        ret =
-           gnutls_privkey_generate(pkey_rsa, GNUTLS_PK_RSA, 2048, 0);
+           gnutls_privkey_generate(pkey, GNUTLS_PK_RSA, 2048, 0);
        if (ret < 0) {
                fail("gnutls_privkey_generate: %s\n", gnutls_strerror(ret));
        }
 
-       assert(gnutls_privkey_set_spki(pkey_rsa, spki, 0)>=0);
-       assert(gnutls_privkey_export_x509(pkey_rsa, &tkey) >=0);
+       assert(gnutls_privkey_set_spki(pkey, spki, 0)>=0);
+       assert(gnutls_privkey_export_x509(pkey, &tkey) >=0);
+       gnutls_x509_spki_deinit(spki);
 
        gnutls_x509_privkey_export2_pkcs8(tkey, GNUTLS_X509_FMT_PEM, NULL, 0, &tmp);
 
        /* import RSA-PSS version of key */
-       assert(gnutls_privkey_init(&pkey_rsa_pss) >=0);
-       assert(gnutls_privkey_import_x509_raw(pkey_rsa_pss, &tmp, GNUTLS_X509_FMT_PEM, NULL, 0) >= 0);
+       assert(gnutls_privkey_init(pkey_rsa_pss) >=0);
+       assert(gnutls_privkey_import_x509_raw(*pkey_rsa_pss, &tmp, GNUTLS_X509_FMT_PEM, NULL, 0) >= 0);
 
        gnutls_free(tmp.data);
 
-       /* import RSA-PSS version of key */
-       gnutls_privkey_deinit(pkey_rsa);
+       /* import RSA version of key */
        gnutls_x509_privkey_export2(tkey, GNUTLS_X509_FMT_PEM, &tmp);
-       assert(gnutls_privkey_init(&pkey_rsa) >=0);
-       assert(gnutls_privkey_import_x509_raw(pkey_rsa, &tmp, GNUTLS_X509_FMT_PEM, NULL, 0) >= 0);
+       assert(gnutls_privkey_init(pkey_rsa) >=0);
+       assert(gnutls_privkey_import_x509_raw(*pkey_rsa, &tmp, GNUTLS_X509_FMT_PEM, NULL, 0) >= 0);
 
        gnutls_x509_privkey_deinit(tkey);
        gnutls_free(tmp.data);
+       gnutls_privkey_deinit(pkey);
+}
+
+void doit(void)
+{
+       gnutls_privkey_t pkey_rsa_pss;
+       gnutls_privkey_t pkey_rsa;
+       gnutls_x509_spki_t spki;
+       int ret;
 
-       sign_verify_data(GNUTLS_SIGN_RSA_PSS_SHA256, pkey_rsa_pss);
-       sign_verify_data(GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, pkey_rsa);
-       sign_verify_data(GNUTLS_SIGN_RSA_PSS_SHA256, pkey_rsa);
+       ret = global_init();
+       if (ret < 0)
+               fail("global_init: %d\n", ret);
+
+       gnutls_global_set_log_function(tls_log_func);
+       if (debug)
+               gnutls_global_set_log_level(4711);
+
+       prepare_keys(&pkey_rsa_pss, &pkey_rsa, GNUTLS_DIG_SHA256, 32);
+
+       sign_verify_data(GNUTLS_SIGN_RSA_PSS_SHA256, pkey_rsa_pss, 0, 0, 0, 0);
+       sign_verify_data(GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, pkey_rsa, 0, 0, 0, 0);
+       sign_verify_data(GNUTLS_SIGN_RSA_PSS_SHA256, pkey_rsa, 0, 0, 0, 0);
 
        if (debug)
                success("success signing with RSA-PSS-SHA256\n");
@@ -188,8 +210,43 @@ void doit(void)
        inv_sign_check(GNUTLS_SIGN_RSA_PSS_SHA384, pkey_rsa, 0);
        inv_sign_check(GNUTLS_SIGN_RSA_PSS_SHA512, pkey_rsa, 0);
 
+       gnutls_privkey_deinit(pkey_rsa_pss);
        gnutls_privkey_deinit(pkey_rsa);
+
+       /* Use the mismatched salt length with the digest length */
+       prepare_keys(&pkey_rsa_pss, &pkey_rsa, GNUTLS_DIG_SHA256, 48);
+
+       sign_verify_data(GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, pkey_rsa_pss,
+                        0, 0, 0, 0);
+       sign_verify_data(GNUTLS_SIGN_RSA_PSS_SHA256, pkey_rsa_pss,
+                        GNUTLS_PRIVKEY_FLAG_RSA_PSS_FIXED_SALT_LENGTH,
+                        0,
+                        GNUTLS_E_CONSTRAINT_ERROR,
+                        0);
+       sign_verify_data(GNUTLS_SIGN_RSA_PSS_SHA256, pkey_rsa_pss,
+                        0,
+                        GNUTLS_VERIFY_RSA_PSS_FIXED_SALT_LENGTH,
+                        0,
+                        GNUTLS_E_PK_SIG_VERIFY_FAILED);
+
+       assert(gnutls_x509_spki_init(&spki)>=0);
+       gnutls_x509_spki_set_rsa_pss_params(spki, GNUTLS_DIG_SHA256, 48);
+       assert(gnutls_privkey_set_spki(pkey_rsa, spki, 0)>=0);
+
+       sign_verify_data(GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, pkey_rsa, 0, 0, 0, 0);
+       sign_verify_data(GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, pkey_rsa,
+                        GNUTLS_PRIVKEY_FLAG_RSA_PSS_FIXED_SALT_LENGTH,
+                        0,
+                        GNUTLS_E_CONSTRAINT_ERROR,
+                        0);
+       sign_verify_data(GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, pkey_rsa,
+                        0,
+                        GNUTLS_VERIFY_RSA_PSS_FIXED_SALT_LENGTH,
+                        0,
+                        GNUTLS_E_PK_SIG_VERIFY_FAILED);
+
        gnutls_privkey_deinit(pkey_rsa_pss);
+       gnutls_privkey_deinit(pkey_rsa);
        gnutls_x509_spki_deinit(spki);
 
        gnutls_global_deinit();