Commit b92e6f5c authored by rsleevi's avatar rsleevi Committed by Commit bot

Detect SHA-1 when it appears in certificate chains

BUG=401365

Review URL: https://codereview.chromium.org/509273002

Cr-Commit-Position: refs/heads/master@{#297307}
parent 396bb736
......@@ -4,6 +4,8 @@
#include "net/cert/cert_verify_proc_android.h"
#include <openssl/x509v3.h>
#include <string>
#include <vector>
......@@ -71,6 +73,36 @@ bool VerifyFromAndroidTrustManager(const std::vector<std::string>& cert_bytes,
verify_result->verified_cert = verified_cert;
}
// Extract the algorithm information from the certs
X509Certificate::OSCertHandles chain;
const X509Certificate::OSCertHandles& intermediates =
verify_result->verified_cert->GetIntermediateCertificates();
chain.push_back(verify_result->verified_cert->os_cert_handle());
chain.insert(chain.end(), intermediates.begin(), intermediates.end());
// If the chain successfully verified, ignore the trust anchor (the last
// certificate). Otherwise, assume the chain is partial. This is not entirely
// correct, as a full chain may have been constructed and then failed to
// validate. However, if that is the case, the more serious error will
// override any SHA-1 considerations.
size_t correction_for_root = (status == android::VERIFY_OK) ? 1 : 0;
for (size_t i = 0; i < chain.size() - correction_for_root; ++i) {
int sig_alg = OBJ_obj2nid(chain[i]->sig_alg->algorithm);
if (sig_alg == NID_md2WithRSAEncryption) {
verify_result->has_md2 = true;
} else if (sig_alg == NID_md4WithRSAEncryption) {
verify_result->has_md4 = true;
} else if (sig_alg == NID_md5WithRSAEncryption ||
sig_alg == NID_md5WithRSA) {
verify_result->has_md5 = true;
} else if (sig_alg == NID_sha1WithRSAEncryption ||
sig_alg == NID_dsaWithSHA || sig_alg == NID_dsaWithSHA1 ||
sig_alg == NID_dsaWithSHA1_2 || sig_alg == NID_sha1WithRSA ||
sig_alg == NID_ecdsa_with_SHA1) {
verify_result->has_sha1 = true;
}
}
// Extract the public key hashes.
for (size_t i = 0; i < verified_chain.size(); i++) {
base::StringPiece spki_bytes;
......
......@@ -227,6 +227,13 @@ void GetCertChainInfo(CFArrayRef cert_chain,
verify_result->has_md4 = true;
} else if (CSSMOIDEqual(alg_oid, &CSSMOID_MD5WithRSA)) {
verify_result->has_md5 = true;
} else if (CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithRSA) ||
CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithRSA_OIW) ||
CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithDSA) ||
CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithDSA_CMS) ||
CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithDSA_JDK) ||
CSSMOIDEqual(alg_oid, &CSSMOID_ECDSA_WithSHA1)) {
verify_result->has_sha1 = true;
}
}
if (!verified_cert)
......
......@@ -203,6 +203,12 @@ void GetCertChainInfo(CERTCertList* cert_list,
case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION:
verify_result->has_md4 = true;
break;
case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE:
case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
verify_result->has_sha1 = true;
break;
default:
break;
}
......
......@@ -117,8 +117,14 @@ void GetCertChainInfo(X509_STORE_CTX* store_ctx,
verify_result->has_md2 = true;
} else if (sig_alg == NID_md4WithRSAEncryption) {
verify_result->has_md4 = true;
} else if (sig_alg == NID_md5WithRSAEncryption) {
} else if (sig_alg == NID_md5WithRSAEncryption ||
sig_alg == NID_md5WithRSA) {
verify_result->has_md5 = true;
} else if (sig_alg == NID_sha1WithRSAEncryption ||
sig_alg == NID_dsaWithSHA || sig_alg == NID_dsaWithSHA1 ||
sig_alg == NID_dsaWithSHA1_2 || sig_alg == NID_sha1WithRSA ||
sig_alg == NID_ecdsa_with_SHA1) {
verify_result->has_sha1 = true;
}
}
}
......
......@@ -1260,13 +1260,18 @@ TEST_F(CertVerifyProcTest, CRLSetLeafSerial) {
}
#endif
enum ExpectedAlgorithms {
EXPECT_MD2 = 1 << 0,
EXPECT_MD4 = 1 << 1,
EXPECT_MD5 = 1 << 2,
EXPECT_SHA1 = 1 << 3
};
struct WeakDigestTestData {
const char* root_cert_filename;
const char* intermediate_cert_filename;
const char* ee_cert_filename;
bool expected_has_md5;
bool expected_has_md4;
bool expected_has_md2;
int expected_algorithms;
};
// GTest 'magic' pretty-printer, so that if/when a test fails, it knows how
......@@ -1323,19 +1328,21 @@ TEST_P(CertVerifyProcWeakDigestTest, Verify) {
NULL,
empty_cert_list_,
&verify_result);
EXPECT_EQ(data.expected_has_md5, verify_result.has_md5);
EXPECT_EQ(data.expected_has_md4, verify_result.has_md4);
EXPECT_EQ(data.expected_has_md2, verify_result.has_md2);
EXPECT_EQ(!!(data.expected_algorithms & EXPECT_MD2), verify_result.has_md2);
EXPECT_EQ(!!(data.expected_algorithms & EXPECT_MD4), verify_result.has_md4);
EXPECT_EQ(!!(data.expected_algorithms & EXPECT_MD5), verify_result.has_md5);
EXPECT_EQ(!!(data.expected_algorithms & EXPECT_SHA1), verify_result.has_sha1);
EXPECT_FALSE(verify_result.is_issued_by_additional_trust_anchor);
// Ensure that MD4 and MD2 are tagged as invalid.
if (data.expected_has_md4 || data.expected_has_md2) {
if (data.expected_algorithms & (EXPECT_MD2 | EXPECT_MD4)) {
EXPECT_EQ(CERT_STATUS_INVALID,
verify_result.cert_status & CERT_STATUS_INVALID);
}
// Ensure that MD5 is flagged as weak.
if (data.expected_has_md5) {
if (data.expected_algorithms & EXPECT_MD5) {
EXPECT_EQ(
CERT_STATUS_WEAK_SIGNATURE_ALGORITHM,
verify_result.cert_status & CERT_STATUS_WEAK_SIGNATURE_ALGORITHM);
......@@ -1348,9 +1355,9 @@ TEST_P(CertVerifyProcWeakDigestTest, Verify) {
// OpenSSL, CryptoAPI, Security.framework) and upon which weak algorithm
// present (MD2, MD4, MD5).
if (data.root_cert_filename) {
if (data.expected_has_md4 || data.expected_has_md2) {
if (data.expected_algorithms & (EXPECT_MD2 | EXPECT_MD4)) {
EXPECT_EQ(ERR_CERT_INVALID, rv);
} else if (data.expected_has_md5) {
} else if (data.expected_algorithms & EXPECT_MD5) {
EXPECT_EQ(ERR_CERT_WEAK_SIGNATURE_ALGORITHM, rv);
} else {
EXPECT_EQ(OK, rv);
......@@ -1371,14 +1378,14 @@ TEST_P(CertVerifyProcWeakDigestTest, Verify) {
// The signature algorithm of the root CA should not matter.
const WeakDigestTestData kVerifyRootCATestData[] = {
{ "weak_digest_md5_root.pem", "weak_digest_sha1_intermediate.pem",
"weak_digest_sha1_ee.pem", false, false, false },
"weak_digest_sha1_ee.pem", EXPECT_SHA1 },
#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ "weak_digest_md4_root.pem", "weak_digest_sha1_intermediate.pem",
"weak_digest_sha1_ee.pem", false, false, false },
"weak_digest_sha1_ee.pem", EXPECT_SHA1 },
#endif
{ "weak_digest_md2_root.pem", "weak_digest_sha1_intermediate.pem",
"weak_digest_sha1_ee.pem", false, false, false },
"weak_digest_sha1_ee.pem", EXPECT_SHA1 },
};
INSTANTIATE_TEST_CASE_P(VerifyRoot, CertVerifyProcWeakDigestTest,
testing::ValuesIn(kVerifyRootCATestData));
......@@ -1386,14 +1393,14 @@ INSTANTIATE_TEST_CASE_P(VerifyRoot, CertVerifyProcWeakDigestTest,
// The signature algorithm of intermediates should be properly detected.
const WeakDigestTestData kVerifyIntermediateCATestData[] = {
{ "weak_digest_sha1_root.pem", "weak_digest_md5_intermediate.pem",
"weak_digest_sha1_ee.pem", true, false, false },
"weak_digest_sha1_ee.pem", EXPECT_MD5 | EXPECT_SHA1 },
#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ "weak_digest_sha1_root.pem", "weak_digest_md4_intermediate.pem",
"weak_digest_sha1_ee.pem", false, true, false },
"weak_digest_sha1_ee.pem", EXPECT_MD4 | EXPECT_SHA1 },
#endif
{ "weak_digest_sha1_root.pem", "weak_digest_md2_intermediate.pem",
"weak_digest_sha1_ee.pem", false, false, true },
"weak_digest_sha1_ee.pem", EXPECT_MD2 | EXPECT_SHA1 },
};
// Disabled on NSS - MD4 is not supported, and MD2 and MD5 are disabled.
#if defined(USE_NSS) || defined(OS_IOS)
......@@ -1409,14 +1416,14 @@ WRAPPED_INSTANTIATE_TEST_CASE_P(
// The signature algorithm of end-entity should be properly detected.
const WeakDigestTestData kVerifyEndEntityTestData[] = {
{ "weak_digest_sha1_root.pem", "weak_digest_sha1_intermediate.pem",
"weak_digest_md5_ee.pem", true, false, false },
"weak_digest_md5_ee.pem", EXPECT_MD5 | EXPECT_SHA1 },
#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ "weak_digest_sha1_root.pem", "weak_digest_sha1_intermediate.pem",
"weak_digest_md4_ee.pem", false, true, false },
"weak_digest_md4_ee.pem", EXPECT_MD4 | EXPECT_SHA1 },
#endif
{ "weak_digest_sha1_root.pem", "weak_digest_sha1_intermediate.pem",
"weak_digest_md2_ee.pem", false, false, true },
"weak_digest_md2_ee.pem", EXPECT_MD2 | EXPECT_SHA1 },
};
// Disabled on NSS - NSS caches chains/signatures in such a way that cannot
// be cleared until NSS is cleanly shutdown, which is not presently supported
......@@ -1433,14 +1440,14 @@ WRAPPED_INSTANTIATE_TEST_CASE_P(MAYBE_VerifyEndEntity,
// Incomplete chains should still report the status of the intermediate.
const WeakDigestTestData kVerifyIncompleteIntermediateTestData[] = {
{ NULL, "weak_digest_md5_intermediate.pem", "weak_digest_sha1_ee.pem",
true, false, false },
EXPECT_MD5 | EXPECT_SHA1 },
#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ NULL, "weak_digest_md4_intermediate.pem", "weak_digest_sha1_ee.pem",
false, true, false },
EXPECT_MD4 | EXPECT_SHA1 },
#endif
{ NULL, "weak_digest_md2_intermediate.pem", "weak_digest_sha1_ee.pem",
false, false, true },
EXPECT_MD2 | EXPECT_SHA1 },
};
// Disabled on NSS - libpkix does not return constructed chains on error,
// preventing us from detecting/inspecting the verified chain.
......@@ -1458,14 +1465,14 @@ WRAPPED_INSTANTIATE_TEST_CASE_P(
// Incomplete chains should still report the status of the end-entity.
const WeakDigestTestData kVerifyIncompleteEETestData[] = {
{ NULL, "weak_digest_sha1_intermediate.pem", "weak_digest_md5_ee.pem",
true, false, false },
EXPECT_MD5 | EXPECT_SHA1 },
#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ NULL, "weak_digest_sha1_intermediate.pem", "weak_digest_md4_ee.pem",
false, true, false },
EXPECT_MD4 | EXPECT_SHA1 },
#endif
{ NULL, "weak_digest_sha1_intermediate.pem", "weak_digest_md2_ee.pem",
false, false, true },
EXPECT_MD2 | EXPECT_SHA1 },
};
// Disabled on NSS - libpkix does not return constructed chains on error,
// preventing us from detecting/inspecting the verified chain.
......@@ -1483,13 +1490,13 @@ WRAPPED_INSTANTIATE_TEST_CASE_P(
// reported.
const WeakDigestTestData kVerifyMixedTestData[] = {
{ "weak_digest_sha1_root.pem", "weak_digest_md5_intermediate.pem",
"weak_digest_md2_ee.pem", true, false, true },
"weak_digest_md2_ee.pem", EXPECT_MD2 | EXPECT_MD5 },
{ "weak_digest_sha1_root.pem", "weak_digest_md2_intermediate.pem",
"weak_digest_md5_ee.pem", true, false, true },
"weak_digest_md5_ee.pem", EXPECT_MD2 | EXPECT_MD5 },
#if defined(USE_OPENSSL_CERTS) || defined(OS_WIN)
// MD4 is not supported by OS X / NSS
{ "weak_digest_sha1_root.pem", "weak_digest_md4_intermediate.pem",
"weak_digest_md2_ee.pem", false, true, true },
"weak_digest_md2_ee.pem", EXPECT_MD2 | EXPECT_MD4 },
#endif
};
// NSS does not support MD4 and does not enable MD2 by default, making all
......
......@@ -334,6 +334,13 @@ void GetCertChainInfo(PCCERT_CHAIN_CONTEXT chain_context,
} else if (strcmp(algorithm, szOID_RSA_MD4RSA) == 0) {
// md4WithRSAEncryption: 1.2.840.113549.1.1.3
verify_result->has_md4 = true;
} else if (strcmp(algorithm, szOID_RSA_SHA1RSA) == 0 ||
strcmp(algorithm, szOID_X957_SHA1DSA) == 0 ||
strcmp(algorithm, szOID_ECDSA_SHA1) == 0) {
// sha1WithRSAEncryption: 1.2.840.113549.1.1.5
// id-dsa-with-sha1: 1.2.840.10040.4.3
// ecdsa-with-SHA1: 1.2.840.10045.4.1
verify_result->has_sha1 = true;
}
}
......
......@@ -18,9 +18,10 @@ CertVerifyResult::~CertVerifyResult() {
void CertVerifyResult::Reset() {
verified_cert = NULL;
cert_status = 0;
has_md5 = false;
has_md2 = false;
has_md4 = false;
has_md5 = false;
has_sha1 = false;
is_issued_by_known_root = false;
is_issued_by_additional_trust_anchor = false;
common_name_fallback_used = false;
......
......@@ -44,9 +44,10 @@ class NET_EXPORT CertVerifyResult {
CertStatus cert_status;
// Properties of the certificate chain.
bool has_md5;
bool has_md2;
bool has_md4;
bool has_md5;
bool has_sha1;
// If the certificate was successfully verified then this contains the
// hashes, in several hash algorithms, of the SubjectPublicKeyInfos of the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment