From 68436f0a8964e911eb4f864bc8b31d7ca4d29585 Mon Sep 17 00:00:00 2001 From: Kurt Roeckx <kurt@roeckx.be> Date: Thu, 2 Jan 2020 23:25:27 +0100 Subject: [PATCH] Stop accepting certificates signed using SHA1 at security level 1 Reviewed-by: Viktor Dukhovni <viktor@openssl.org> GH: #10786 (cherry picked from commit b744f915ca8bb37631909728dd2529289bda8438) --- CHANGES | 12 ++++++++++++ NEWS | 5 ++++- crypto/rsa/rsa_ameth.c | 20 +++++++++++++++++++- crypto/x509/x509_set.c | 14 ++++++++++++++ test/recipes/25-test_verify.t | 8 ++++---- 5 files changed, 53 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index e4d57bb6b5..34d09c5d3c 100644 --- a/CHANGES +++ b/CHANGES @@ -9,6 +9,18 @@ Changes between 1.1.1d and 1.1.1e [xx XXX xxxx] + *) X509 certificates signed using SHA1 are no longer allowed at security + level 1 and above. + In TLS/SSL the default security level is 1. It can be set either + using the cipher string with @SECLEVEL, or calling + SSL_CTX_set_security_level(). If the leaf certificate is signed with SHA-1, + a call to SSL_CTX_use_certificate() will fail if the security level is not + lowered first. + Outside TLS/SSL, the default security level is -1 (effectively 0). It can + be set using X509_VERIFY_PARAM_set_auth_level() or using the -auth_level + options of the apps. + [Kurt Roeckx] + *) Corrected the documentation of the return values from the EVP_DigestSign* set of functions. The documentation mentioned negative values for some errors, but this was never the case, so the mention of negative values diff --git a/NEWS b/NEWS index 4af390505d..11840cf05b 100644 --- a/NEWS +++ b/NEWS @@ -7,7 +7,10 @@ Major changes between OpenSSL 1.1.1d and OpenSSL 1.1.1e [under development] - o + o X509 certificates signed using SHA1 are no longer allowed at security + level 1 or higher. The default security level for TLS is 1, so + certificates signed using SHA1 are by default no longer trusted to + authenticate servers or clients. Major changes between OpenSSL 1.1.1c and OpenSSL 1.1.1d [10 Sep 2019] diff --git a/crypto/rsa/rsa_ameth.c b/crypto/rsa/rsa_ameth.c index 6692a51ed8..d45d6b5ba3 100644 --- a/crypto/rsa/rsa_ameth.c +++ b/crypto/rsa/rsa_ameth.c @@ -855,6 +855,7 @@ static int rsa_sig_info_set(X509_SIG_INFO *siginf, const X509_ALGOR *sigalg, uint32_t flags; const EVP_MD *mgf1md = NULL, *md = NULL; RSA_PSS_PARAMS *pss; + int secbits; /* Sanity check: make sure it is PSS */ if (OBJ_obj2nid(sigalg->algorithm) != EVP_PKEY_RSA_PSS) @@ -874,7 +875,24 @@ static int rsa_sig_info_set(X509_SIG_INFO *siginf, const X509_ALGOR *sigalg, else flags = 0; /* Note: security bits half number of digest bits */ - X509_SIG_INFO_set(siginf, mdnid, EVP_PKEY_RSA_PSS, EVP_MD_size(md) * 4, + secbits = EVP_MD_size(md) * 4; + /* + * SHA1 and MD5 are known to be broken. Reduce security bits so that + * they're no longer accepted at security level 1. The real values don't + * really matter as long as they're lower than 80, which is our security + * level 1. + * https://eprint.iacr.org/2020/014 puts a chosen-prefix attack for SHA1 at + * 2^63.4 + * https://documents.epfl.ch/users/l/le/lenstra/public/papers/lat.pdf + * puts a chosen-prefix attack for MD5 at 2^39. + */ + if (mdnid == NID_sha1) + secbits = 64; + else if (mdnid == NID_md5_sha1) + secbits = 68; + else if (mdnid == NID_md5) + secbits = 39; + X509_SIG_INFO_set(siginf, mdnid, EVP_PKEY_RSA_PSS, secbits, flags); rv = 1; err: diff --git a/crypto/x509/x509_set.c b/crypto/x509/x509_set.c index 164b4e2be1..deb7722c18 100644 --- a/crypto/x509/x509_set.c +++ b/crypto/x509/x509_set.c @@ -222,6 +222,20 @@ static void x509_sig_info_init(X509_SIG_INFO *siginf, const X509_ALGOR *alg, return; /* Security bits: half number of bits in digest */ siginf->secbits = EVP_MD_size(md) * 4; + /* + * SHA1 and MD5 are known to be broken. Reduce security bits so that + * they're no longer accepted at security level 1. The real values don't + * really matter as long as they're lower than 80, which is our security + * level 1. + * https://eprint.iacr.org/2020/014 puts a chosen-prefix attack for SHA1 at + * 2^63.4 + * https://documents.epfl.ch/users/l/le/lenstra/public/papers/lat.pdf + * puts a chosen-prefix attack for MD5 at 2^39. + */ + if (mdnid == NID_sha1) + siginf->secbits = 63; + else if (mdnid == NID_md5) + siginf->secbits = 39; switch (mdnid) { case NID_sha1: case NID_sha256: diff --git a/test/recipes/25-test_verify.t b/test/recipes/25-test_verify.t index b80a1cde3e..5e5bc9ef1e 100644 --- a/test/recipes/25-test_verify.t +++ b/test/recipes/25-test_verify.t @@ -336,14 +336,14 @@ ok(!verify("badalt9-cert", "sslserver", ["root-cert"], ["ncca1-cert", "ncca3-cer ok(!verify("badalt10-cert", "sslserver", ["root-cert"], ["ncca1-cert", "ncca3-cert"], ), "Name constraints nested DNS name excluded"); -ok(verify("ee-pss-sha1-cert", "sslserver", ["root-cert"], ["ca-cert"], ), - "Certificate PSS signature using SHA1"); +ok(verify("ee-pss-sha1-cert", "sslserver", ["root-cert"], ["ca-cert"], "-auth_level", "0"), + "Accept PSS signature using SHA1 at auth level 0"); ok(verify("ee-pss-sha256-cert", "sslserver", ["root-cert"], ["ca-cert"], ), "CA with PSS signature using SHA256"); -ok(!verify("ee-pss-sha1-cert", "sslserver", ["root-cert"], ["ca-cert"], "-auth_level", "2"), - "Reject PSS signature using SHA1 and auth level 2"); +ok(!verify("ee-pss-sha1-cert", "sslserver", ["root-cert"], ["ca-cert"], "-auth_level", "1"), + "Reject PSS signature using SHA1 and auth level 1"); ok(verify("ee-pss-sha256-cert", "sslserver", ["root-cert"], ["ca-cert"], "-auth_level", "2"), "PSS signature using SHA256 and auth level 2"); -- 2.25.1