Bug 1183822 - fix OCSP verification failures (r=keeler)
☠☠ backed out by 99b36484d3bc ☠ ☠
authorMark Goodwin <mgoodwin@mozilla.com>
Fri, 17 Jul 2015 10:03:56 +0100
changeset 253457 fb6cbb4ada544b1d4e690b8dad1e067c2e3e609b
parent 253456 ec1b5a7d05e941f4b4bb4ef38d949011f877124f
child 253458 f324dcfaab40316a32888591053bd27b4c353658
push id29065
push userryanvm@gmail.com
push dateFri, 17 Jul 2015 14:26:32 +0000
treeherdermozilla-central@911935404233 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1183822
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1183822 - fix OCSP verification failures (r=keeler) Adds a new TrustDomain for OCSP Signers which will always allow all acceptible signature digest algorithms. Calls to most other TrustDomain methods are passed through to the owning NSSCertDBTrustDomain.
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/OCSPVerificationTrustDomain.cpp
security/certverifier/OCSPVerificationTrustDomain.h
security/certverifier/moz.build
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "NSSCertDBTrustDomain.h"
 
 #include <stdint.h>
 
 #include "ExtendedValidation.h"
 #include "OCSPRequestor.h"
+#include "OCSPVerificationTrustDomain.h"
 #include "certdb.h"
 #include "cert.h"
 #include "mozilla/UniquePtr.h"
 #include "nsNSSCertificate.h"
 #include "nss.h"
 #include "NSSErrorsService.h"
 #include "nsServiceManagerUtils.h"
 #include "pk11pub.h"
@@ -633,17 +634,27 @@ NSSCertDBTrustDomain::CheckRevocation(En
 Result
 NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
   const CertID& certID, Time time, uint16_t maxLifetimeInDays,
   Input encodedResponse, EncodedResponseSource responseSource,
   /*out*/ bool& expired)
 {
   Time thisUpdate(Time::uninitialized);
   Time validThrough(Time::uninitialized);
-  Result rv = VerifyEncodedOCSPResponse(*this, certID, time,
+
+  // We use a try and fallback approach which first mandates good signature
+  // digest algorithms, then falls back to SHA-1 if this fails. If a delegated
+  // OCSP response signing certificate was issued with a SHA-1 signature,
+  // verification initially fails. We cache the failure and then re-use that
+  // result even when doing fallback (i.e. when weak signature digest algorithms
+  // should succeed). To address this we use an OCSPVerificationTrustDomain
+  // here, rather than using *this, to ensure verification succeeds for all
+  // allowed signature digest algorithms.
+  OCSPVerificationTrustDomain trustDomain(*this);
+  Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, time,
                                         maxLifetimeInDays, encodedResponse,
                                         expired, &thisUpdate, &validThrough);
   // If a response was stapled and expired, we don't want to cache it. Return
   // early to simplify the logic here.
   if (responseSource == ResponseWasStapled && expired) {
     PR_ASSERT(rv != Success);
     return rv;
   }
new file mode 100644
--- /dev/null
+++ b/security/certverifier/OCSPVerificationTrustDomain.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OCSPVerificationTrustDomain.h"
+
+using namespace mozilla;
+using namespace mozilla::pkix;
+
+OCSPVerificationTrustDomain::OCSPVerificationTrustDomain(
+  NSSCertDBTrustDomain& certDBTrustDomain)
+  : mCertDBTrustDomain(certDBTrustDomain)
+{
+}
+
+Result
+OCSPVerificationTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
+                                          const CertPolicyId& policy,
+                                          Input candidateCertDER,
+                                  /*out*/ TrustLevel& trustLevel)
+{
+  return mCertDBTrustDomain.GetCertTrust(endEntityOrCA, policy,
+                                         candidateCertDER, trustLevel);
+}
+
+
+Result
+OCSPVerificationTrustDomain::FindIssuer(Input, IssuerChecker&, Time)
+{
+  // We do not expect this to be called for OCSP signers
+  return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+Result
+OCSPVerificationTrustDomain::IsChainValid(const DERArray&, Time)
+{
+  // We do not expect this to be called for OCSP signers
+  return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+Result
+OCSPVerificationTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&,
+                                             Time, Duration, const Input*,
+                                             const Input*)
+{
+  // We do not expect this to be called for OCSP signers
+  return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
+Result
+OCSPVerificationTrustDomain::CheckSignatureDigestAlgorithm(
+  DigestAlgorithm aAlg, EndEntityOrCA aEEOrCA)
+{
+  // The reason for wrapping the NSSCertDBTrustDomain in an
+  // OCSPVerificationTrustDomain is to allow us to bypass the weaker signature
+  // algorithm check - thus all allowable signature digest algorithms should
+  // always be accepted. This is only needed while we gather telemetry on SHA-1.
+  return Success;
+}
+
+Result
+OCSPVerificationTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
+  EndEntityOrCA aEEOrCA, unsigned int aModulusSizeInBits)
+{
+  return mCertDBTrustDomain.
+      CheckRSAPublicKeyModulusSizeInBits(aEEOrCA, aModulusSizeInBits);
+}
+
+Result
+OCSPVerificationTrustDomain::VerifyRSAPKCS1SignedDigest(
+  const SignedDigest& aSignedDigest, Input aSubjectPublicKeyInfo)
+{
+  return mCertDBTrustDomain.VerifyRSAPKCS1SignedDigest(aSignedDigest,
+                                                       aSubjectPublicKeyInfo);
+}
+
+Result
+OCSPVerificationTrustDomain::CheckECDSACurveIsAcceptable(
+  EndEntityOrCA aEEOrCA, NamedCurve aCurve)
+{
+  return mCertDBTrustDomain.CheckECDSACurveIsAcceptable(aEEOrCA, aCurve);
+}
+
+Result
+OCSPVerificationTrustDomain::VerifyECDSASignedDigest(
+  const SignedDigest& aSignedDigest, Input aSubjectPublicKeyInfo)
+{
+  return mCertDBTrustDomain.VerifyECDSASignedDigest(aSignedDigest,
+                                                    aSubjectPublicKeyInfo);
+}
+
+Result
+OCSPVerificationTrustDomain::CheckValidityIsAcceptable(
+  Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA,
+  KeyPurposeId keyPurpose)
+{
+  return mCertDBTrustDomain.CheckValidityIsAcceptable(notBefore, notAfter,
+                                                      endEntityOrCA,
+                                                      keyPurpose);
+}
+
+Result
+OCSPVerificationTrustDomain::DigestBuf(
+  Input item, DigestAlgorithm digestAlg,
+  /*out*/ uint8_t* digestBuf, size_t digestBufLen)
+{
+  return mCertDBTrustDomain.DigestBuf(item, digestAlg, digestBuf, digestBufLen);
+}
new file mode 100644
--- /dev/null
+++ b/security/certverifier/OCSPVerificationTrustDomain.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_psm__OCSPVerificationTrustDomain_h
+#define mozilla_psm__OCSPVerificationTrustDomain_h
+
+#include "pkix/pkixtypes.h"
+#include "NSSCertDBTrustDomain.h"
+
+namespace mozilla { namespace psm {
+
+class OCSPVerificationTrustDomain : public mozilla::pkix::TrustDomain
+{
+public:
+  OCSPVerificationTrustDomain(NSSCertDBTrustDomain& certDBTrustDomain);
+
+  virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
+                            IssuerChecker& checker,
+                            mozilla::pkix::Time time) override;
+
+  virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                              const mozilla::pkix::CertPolicyId& policy,
+                              mozilla::pkix::Input candidateCertDER,
+                      /*out*/ mozilla::pkix::TrustLevel& trustLevel)
+                              override;
+
+  virtual Result CheckSignatureDigestAlgorithm(
+                   mozilla::pkix::DigestAlgorithm digestAlg,
+                   mozilla::pkix::EndEntityOrCA endEntityOrCA) override;
+
+  virtual Result CheckRSAPublicKeyModulusSizeInBits(
+                   mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                   unsigned int modulusSizeInBits) override;
+
+  virtual Result VerifyRSAPKCS1SignedDigest(
+                   const mozilla::pkix::SignedDigest& signedDigest,
+                   mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+  virtual Result CheckECDSACurveIsAcceptable(
+                   mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                   mozilla::pkix::NamedCurve curve) override;
+
+  virtual Result VerifyECDSASignedDigest(
+                   const mozilla::pkix::SignedDigest& signedDigest,
+                   mozilla::pkix::Input subjectPublicKeyInfo) override;
+
+  virtual Result DigestBuf(mozilla::pkix::Input item,
+                           mozilla::pkix::DigestAlgorithm digestAlg,
+                   /*out*/ uint8_t* digestBuf,
+                           size_t digestBufLen) override;
+
+  virtual Result CheckValidityIsAcceptable(
+                   mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
+                   mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                   mozilla::pkix::KeyPurposeId keyPurpose) override;
+
+  virtual Result CheckRevocation(
+                   mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                   const mozilla::pkix::CertID& certID,
+                   mozilla::pkix::Time time,
+                   mozilla::pkix::Duration validityDuration,
+      /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
+      /*optional*/ const mozilla::pkix::Input* aiaExtension)
+                   override;
+
+  virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
+                              mozilla::pkix::Time time) override;
+
+private:
+  NSSCertDBTrustDomain& mCertDBTrustDomain;
+};
+
+
+} } // namespace mozilla::psm
+
+#endif // mozilla_psm__OCSPVerificationTrustDomain_h
--- a/security/certverifier/moz.build
+++ b/security/certverifier/moz.build
@@ -9,16 +9,17 @@ EXPORTS += [
     'OCSPCache.h',
 ]
 
 UNIFIED_SOURCES += [
     'CertVerifier.cpp',
     'NSSCertDBTrustDomain.cpp',
     'OCSPCache.cpp',
     'OCSPRequestor.cpp',
+    'OCSPVerificationTrustDomain.cpp',
 ]
 
 if not CONFIG['NSS_NO_EV_CERTS']:
     UNIFIED_SOURCES += [
         'ExtendedValidation.cpp',
     ]
 
 LOCAL_INCLUDES += [