From: Daiki Ueno Date: Mon, 2 Aug 2021 16:32:28 +0000 (+0200) Subject: pk: add flags to force RSA-PSS salt length to match digest length X-Git-Tag: 3.7.3~53^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3cdbb84fab1dfbe157804eb72e279265eaaa2cb7;p=thirdparty%2Fgnutls.git pk: add flags to force RSA-PSS salt length to match digest length 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 --- diff --git a/NEWS b/NEWS index b13c97b35a..0fcd043aa3 100644 --- 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 diff --git a/lib/crypto-backend.h b/lib/crypto-backend.h index 33005e73dd..6cc1853cbe 100644 --- a/lib/crypto-backend.h +++ b/lib/crypto-backend.h @@ -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 { \ diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h index 7e98ea2b13..c9f8067c13 100644 --- a/lib/includes/gnutls/abstract.h +++ b/lib/includes/gnutls/abstract.h @@ -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, diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index 5f54e888bc..7953a30460 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -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; diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c index ff8e3d15b4..16d9b4a04c 100644 --- a/lib/nettle/pk.c +++ b/lib/nettle/pk.c @@ -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 diff --git a/lib/privkey.c b/lib/privkey.c index 9f02c5b062..0e8d29561f 100644 --- a/lib/privkey.c +++ b/lib/privkey.c @@ -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; diff --git a/lib/pubkey.c b/lib/pubkey.c index 5fe30f798c..a1735cf766 100644 --- a/lib/pubkey.c +++ b/lib/pubkey.c @@ -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)) diff --git a/lib/tls13-sig.c b/lib/tls13-sig.c index a52295faca..3b193d71e7 100644 --- a/lib/tls13-sig.c +++ b/lib/tls13-sig.c @@ -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; diff --git a/tests/rsa-rsa-pss.c b/tests/rsa-rsa-pss.c index 7ba66803d5..19a175b722 100644 --- a/tests/rsa-rsa-pss.c +++ b/tests/rsa-rsa-pss.c @@ -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();