Bug 1130754: Avoid recalculating tbsCertificate digest, r=keeler
authorBrian Smith <brian@briansmith.org>
Sat, 07 Feb 2015 12:14:31 -0800
changeset 257364 5e39cbc525ad091f8ee8cd2a9fbfcf49f3e89c36
parent 257363 8ed8507adfcf2c72c30c5715243efdaa28497cb4
child 257365 20f6c0ee944d287300eae04e2d32ab167d1a4668
push id721
push userjlund@mozilla.com
push dateTue, 21 Apr 2015 23:03:33 +0000
treeherdermozilla-release@d27c9211ebb3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1130754
milestone38.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 1130754: Avoid recalculating tbsCertificate digest, r=keeler
config/external/nss/nss.def
security/apps/AppTrustDomain.cpp
security/apps/AppTrustDomain.h
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/NSSCertDBTrustDomain.h
security/pkix/include/pkix/pkixnss.h
security/pkix/include/pkix/pkixtypes.h
security/pkix/lib/pkixbuild.cpp
security/pkix/lib/pkixcert.cpp
security/pkix/lib/pkixder.cpp
security/pkix/lib/pkixder.h
security/pkix/lib/pkixnss.cpp
security/pkix/lib/pkixocsp.cpp
security/pkix/lib/pkixutil.h
security/pkix/lib/pkixverify.cpp
security/pkix/moz.build
security/pkix/test/gtest/pkixbuild_tests.cpp
security/pkix/test/gtest/pkixcert_extension_tests.cpp
security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp
security/pkix/test/gtest/pkixder_pki_types_tests.cpp
security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
security/pkix/test/lib/pkixtestnss.cpp
security/pkix/test/lib/pkixtestutil.h
--- a/config/external/nss/nss.def
+++ b/config/external/nss/nss.def
@@ -672,12 +672,12 @@ SSL_VersionRangeSet
 SSL_VersionRangeSetDefault
 UTIL_SetForkState
 VFY_Begin
 VFY_CreateContext
 VFY_DestroyContext
 VFY_End
 VFY_Update
 VFY_VerifyData
-VFY_VerifyDataDirect
 VFY_VerifyDataWithAlgorithmID
+VFY_VerifyDigestDirect
 _SGN_VerifyPKCS1DigestInfo
 PK11_PQG_ParamGenV2
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -209,27 +209,22 @@ AppTrustDomain::GetCertTrust(EndEntityOr
     return Success;
   }
 
   trustLevel = TrustLevel::InheritsTrust;
   return Success;
 }
 
 Result
-AppTrustDomain::VerifySignedData(const SignedDataWithSignature& signedData,
-                                 Input subjectPublicKeyInfo)
-{
-  return VerifySignedDataNSS(signedData, subjectPublicKeyInfo, mPinArg);
-}
-
-Result
-AppTrustDomain::DigestBuf(Input item, /*out*/ uint8_t* digestBuf,
+AppTrustDomain::DigestBuf(Input item,
+                          DigestAlgorithm digestAlg,
+                          /*out*/ uint8_t* digestBuf,
                           size_t digestBufLen)
 {
-  return DigestBufNSS(item, digestBuf, digestBufLen);
+  return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
 }
 
 Result
 AppTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, Time,
                                 /*optional*/ const Input*,
                                 /*optional*/ const Input*)
 {
   // We don't currently do revocation checking. If we need to distrust an Apps
@@ -254,22 +249,38 @@ AppTrustDomain::CheckRSAPublicKeyModulus
 {
   if (modulusSizeInBits < mMinimumNonECCBits) {
     return Result::ERROR_INADEQUATE_KEY_SIZE;
   }
   return Success;
 }
 
 Result
+AppTrustDomain::VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+                                           Input subjectPublicKeyInfo)
+{
+  return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+                                       mPinArg);
+}
+
+Result
 AppTrustDomain::CheckECDSACurveIsAcceptable(EndEntityOrCA /*endEntityOrCA*/,
                                             NamedCurve curve)
 {
   switch (curve) {
     case NamedCurve::secp256r1: // fall through
     case NamedCurve::secp384r1: // fall through
     case NamedCurve::secp521r1:
       return Success;
   }
 
   return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
 }
 
+Result
+AppTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                                        Input subjectPublicKeyInfo)
+{
+  return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+                                    mPinArg);
+}
+
 } } // namespace mozilla::psm
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -36,23 +36,27 @@ public:
                                  mozilla::pkix::Time time,
                     /*optional*/ const mozilla::pkix::Input* stapledOCSPresponse,
                     /*optional*/ const mozilla::pkix::Input* aiaExtension) MOZ_OVERRIDE;
   virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
                               mozilla::pkix::Time time) MOZ_OVERRIDE;
   virtual Result CheckRSAPublicKeyModulusSizeInBits(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    unsigned int modulusSizeInBits) MOZ_OVERRIDE;
+  virtual Result VerifyRSAPKCS1SignedDigest(
+                   const mozilla::pkix::SignedDigest& signedDigest,
+                   mozilla::pkix::Input subjectPublicKeyInfo) MOZ_OVERRIDE;
   virtual Result CheckECDSACurveIsAcceptable(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    mozilla::pkix::NamedCurve curve) MOZ_OVERRIDE;
-  virtual Result VerifySignedData(
-           const mozilla::pkix::SignedDataWithSignature& signedData,
-           mozilla::pkix::Input subjectPublicKeyInfo) MOZ_OVERRIDE;
+  virtual Result VerifyECDSASignedDigest(
+                   const mozilla::pkix::SignedDigest& signedDigest,
+                   mozilla::pkix::Input subjectPublicKeyInfo) MOZ_OVERRIDE;
   virtual Result DigestBuf(mozilla::pkix::Input item,
+                           mozilla::pkix::DigestAlgorithm digestAlg,
                            /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) MOZ_OVERRIDE;
 
 private:
   /*out*/ ScopedCERTCertList& mCertChain;
   void* mPinArg; // non-owning!
   ScopedCERTCertificate mTrustedRoot;
   unsigned int mMinimumNonECCBits;
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -252,27 +252,20 @@ NSSCertDBTrustDomain::GetCertTrust(EndEn
     }
   }
 
   trustLevel = TrustLevel::InheritsTrust;
   return Success;
 }
 
 Result
-NSSCertDBTrustDomain::VerifySignedData(const SignedDataWithSignature& signedData,
-                                       Input subjectPublicKeyInfo)
-{
-  return VerifySignedDataNSS(signedData, subjectPublicKeyInfo, mPinArg);
-}
-
-Result
-NSSCertDBTrustDomain::DigestBuf(Input item,
+NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
                                 /*out*/ uint8_t* digestBuf, size_t digestBufLen)
 {
-  return DigestBufNSS(item, digestBuf, digestBufLen);
+  return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
 }
 
 
 static PRIntervalTime
 OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching)
 {
   switch (ocspFetching) {
     case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
@@ -725,29 +718,46 @@ NSSCertDBTrustDomain::CheckRSAPublicKeyM
 {
   if (modulusSizeInBits < mMinimumNonECCBits) {
     return Result::ERROR_INADEQUATE_KEY_SIZE;
   }
   return Success;
 }
 
 Result
+NSSCertDBTrustDomain::VerifyRSAPKCS1SignedDigest(
+  const SignedDigest& signedDigest,
+  Input subjectPublicKeyInfo)
+{
+  return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+                                       mPinArg);
+}
+
+Result
 NSSCertDBTrustDomain::CheckECDSACurveIsAcceptable(
   EndEntityOrCA /*endEntityOrCA*/, NamedCurve curve)
 {
   switch (curve) {
     case NamedCurve::secp256r1: // fall through
     case NamedCurve::secp384r1: // fall through
     case NamedCurve::secp521r1:
       return Success;
   }
 
   return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
 }
 
+Result
+NSSCertDBTrustDomain::VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                                              Input subjectPublicKeyInfo)
+{
+  return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+                                    mPinArg);
+}
+
 namespace {
 
 static char*
 nss_addEscape(const char* string, char quote)
 {
   char* newString = 0;
   size_t escapes = 0, size = 0;
   const char* src;
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -69,26 +69,30 @@ public:
                               mozilla::pkix::Input candidateCertDER,
                               /*out*/ mozilla::pkix::TrustLevel& trustLevel)
                               MOZ_OVERRIDE;
 
   virtual Result CheckRSAPublicKeyModulusSizeInBits(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    unsigned int modulusSizeInBits) MOZ_OVERRIDE;
 
+  virtual Result VerifyRSAPKCS1SignedDigest(
+                   const mozilla::pkix::SignedDigest& signedDigest,
+                   mozilla::pkix::Input subjectPublicKeyInfo) MOZ_OVERRIDE;
+
   virtual Result CheckECDSACurveIsAcceptable(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    mozilla::pkix::NamedCurve curve) MOZ_OVERRIDE;
 
-  virtual Result VerifySignedData(
-                   const mozilla::pkix::SignedDataWithSignature& signedData,
-                   mozilla::pkix::Input subjectPublicKeyInfo)
-                   MOZ_OVERRIDE;
+  virtual Result VerifyECDSASignedDigest(
+                   const mozilla::pkix::SignedDigest& signedDigest,
+                   mozilla::pkix::Input subjectPublicKeyInfo) MOZ_OVERRIDE;
 
   virtual Result DigestBuf(mozilla::pkix::Input item,
+                           mozilla::pkix::DigestAlgorithm digestAlg,
                            /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) MOZ_OVERRIDE;
 
   virtual Result CheckRevocation(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    const mozilla::pkix::CertID& certID,
                    mozilla::pkix::Time time,
       /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
--- a/security/pkix/include/pkix/pkixnss.h
+++ b/security/pkix/include/pkix/pkixnss.h
@@ -26,33 +26,42 @@
 #define mozilla_pkix_pkixnss_h
 
 #include "pkixtypes.h"
 #include "prerror.h"
 #include "seccomon.h"
 
 namespace mozilla { namespace pkix {
 
-// Verify the given signed data using the given public key.
-Result VerifySignedDataNSS(const SignedDataWithSignature& sd,
-                           Input subjectPublicKeyInfo,
-                           void* pkcs11PinArg);
+// Verifies the PKCS#1.5 signature on the given data using the given RSA public
+// key.
+Result VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd,
+                                     Input subjectPublicKeyInfo,
+                                     void* pkcs11PinArg);
 
-// Computes the SHA-1 hash of the data in the current item.
+// Verifies the ECDSA signature on the given data using the given ECC public
+// key.
+Result VerifyECDSASignedDigestNSS(const SignedDigest& sd,
+                                  Input subjectPublicKeyInfo,
+                                  void* pkcs11PinArg);
+
+// Computes the digest of the given data using the given digest algorithm.
 //
 // item contains the data to hash.
-// digestBuf must point to a buffer to where the SHA-1 hash will be written.
-// digestBufLen must be 20 (the length of a SHA-1 hash,
-//              TrustDomain::DIGEST_LENGTH).
+// digestBuf must point to a buffer to where the digest will be written.
+// digestBufLen must be the size of the buffer, which must be exactly equal
+//              to the size of the digest output (20 for SHA-1, 32 for SHA-256,
+//              etc.)
 //
-// TODO(bug 966856): Add SHA-2 support
 // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our
 // other, extensive, memory safety efforts in mozilla::pkix, and we should find
 // a way to provide a more-obviously-safe interface.
-Result DigestBufNSS(Input item, /*out*/ uint8_t* digestBuf,
+Result DigestBufNSS(Input item,
+                    DigestAlgorithm digestAlg,
+                    /*out*/ uint8_t* digestBuf,
                     size_t digestBufLen);
 
 Result MapPRErrorCodeToResult(PRErrorCode errorCode);
 PRErrorCode MapResultToPRErrorCode(Result result);
 
 // The error codes within each module must fit in 16 bits. We want these
 // errors to fit in the same module as the NSS errors but not overlap with
 // any of them. Converting an NSS SEC, NSS SSL, or PSM error to an NS error
@@ -78,14 +87,16 @@ void RegisterErrorTable();
 
 inline SECItem UnsafeMapInputToSECItem(Input input)
 {
   SECItem result = {
     siBuffer,
     const_cast<uint8_t*>(input.UnsafeGetData()),
     input.GetLength()
   };
+  static_assert(sizeof(decltype(input.GetLength())) <= sizeof(result.len),
+                "input.GetLength() must fit in a SECItem");
   return result;
 }
 
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix_pkixnss_h
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -46,54 +46,23 @@ enum class NamedCurve
 
   // secp384r1 (OID 1.3.132.0.34, RFC 5480)
   secp384r1 = 2,
 
   // secp256r1 (OID 1.2.840.10045.3.1.7, RFC 5480)
   secp256r1 = 3,
 };
 
-enum class SignatureAlgorithm
+struct SignedDigest final
 {
-  // ecdsa-with-SHA512 (OID 1.2.840.10045.4.3.4, RFC 5758 Section 3.2)
-  ecdsa_with_sha512 = 1,
-
-  // ecdsa-with-SHA384 (OID 1.2.840.10045.4.3.3, RFC 5758 Section 3.2)
-  ecdsa_with_sha384 = 4,
-
-  // ecdsa-with-SHA256 (OID 1.2.840.10045.4.3.2, RFC 5758 Section 3.2)
-  ecdsa_with_sha256 = 7,
-
-  // ecdsa-with-SHA1 (OID 1.2.840.10045.4.1, RFC 3279 Section 2.2.3)
-  ecdsa_with_sha1 = 10,
-
-  // sha512WithRSAEncryption (OID 1.2.840.113549.1.1.13, RFC 4055 Section 5)
-  rsa_pkcs1_with_sha512 = 13,
-
-  // sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12, RFC 4055 Section 5)
-  rsa_pkcs1_with_sha384 = 14,
-
-  // sha256WithRSAEncryption (OID 1.2.840.113549.1.1.11, RFC 4055 Section 5)
-  rsa_pkcs1_with_sha256 = 15,
-
-  // sha-1WithRSAEncryption (OID 1.2.840.113549.1.1.5, RFC 3279 Section 2.2.1)
-  rsa_pkcs1_with_sha1 = 16,
-
-  // Used to indicate any unsupported algorithm.
-  unsupported_algorithm = 19,
-};
-
-struct SignedDataWithSignature final
-{
-public:
-  Input data;
-  SignatureAlgorithm algorithm;
+  Input digest;
+  DigestAlgorithm digestAlgorithm;
   Input signature;
 
-  void operator=(const SignedDataWithSignature&) = delete;
+  void operator=(const SignedDigest&) = delete;
 };
 
 enum class EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 };
 
 enum class KeyUsage : uint8_t
 {
   digitalSignature = 0,
   nonRepudiation   = 1,
@@ -306,49 +275,58 @@ public:
   //
   // Return Success if the key size is acceptable,
   // Result::ERROR_INADEQUATE_KEY_SIZE if the key size is not acceptable,
   // or another error code if another error occurred.
   virtual Result CheckRSAPublicKeyModulusSizeInBits(
                    EndEntityOrCA endEntityOrCA,
                    unsigned int modulusSizeInBits) = 0;
 
+  // Verify the given RSA PKCS#1.5 signature on the given digest using the
+  // given RSA public key.
+  //
+  // CheckRSAPublicKeyModulusSizeInBits will be called before calling this
+  // function, so it is not necessary to repeat those checks here. However,
+  // VerifyRSAPKCS1SignedDigest *is* responsible for doing the mathematical
+  // verification of the public key validity as specified in NIST SP 800-56A.
+  virtual Result VerifyRSAPKCS1SignedDigest(
+                   const SignedDigest& signedDigest,
+                   Input subjectPublicKeyInfo) = 0;
+
   // Check that the given named ECC curve is acceptable for ECDSA signatures.
   //
   // Return Success if the curve is acceptable,
   // Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE if the curve is not acceptable,
   // or another error code if another error occurred.
   virtual Result CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA,
                                              NamedCurve curve) = 0;
 
-  // Verify the given signature using the given public key.
-  //
-  // Most implementations of this function should probably forward the call
-  // directly to mozilla::pkix::VerifySignedData.
+  // Verify the given ECDSA signature on the given digest using the given ECC
+  // public key.
   //
-  // CheckRSAPublicKeyModulusSizeInBits or CheckECDSACurveIsAcceptable will
-  // be called before calling this function, so it is not necessary to repeat
-  // those checks in VerifySignedData. However, VerifySignedData *is*
-  // responsible for doing the mathematical verification of the public key
-  // validity as specified in NIST SP 800-56A.
-  virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
-                                  Input subjectPublicKeyInfo) = 0;
+  // CheckECDSACurveIsAcceptable will be called before calling this function,
+  // so it is not necessary to repeat that check here. However,
+  // VerifyECDSASignedDigest *is* responsible for doing the mathematical
+  // verification of the public key validity as specified in NIST SP 800-56A.
+  virtual Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                                         Input subjectPublicKeyInfo) = 0;
 
-  // Compute the SHA-1 hash of the data in the current item.
+  // Compute a digest of the data in item using the given digest algorithm.
   //
   // item contains the data to hash.
-  // digestBuf must point to a buffer to where the SHA-1 hash will be written.
-  // digestBufLen must be DIGEST_LENGTH (20, the length of a SHA-1 hash).
+  // digestBuf points to a buffer to where the digest will be written.
+  // digestBufLen will be the size of the digest output (20 for SHA-1,
+  // 32 for SHA-256, etc.).
   //
-  // TODO(bug 966856): Add SHA-2 support
   // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our
   // other, extensive, memory safety efforts in mozilla::pkix, and we should
   // find a way to provide a more-obviously-safe interface.
-  static const size_t DIGEST_LENGTH = 20; // length of SHA-1 digest
-  virtual Result DigestBuf(Input item, /*out*/ uint8_t* digestBuf,
+  virtual Result DigestBuf(Input item,
+                           DigestAlgorithm digestAlg,
+                           /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) = 0;
 protected:
   TrustDomain() { }
 
   TrustDomain(const TrustDomain&) = delete;
   void operator=(const TrustDomain&) = delete;
 };
 
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -75,16 +75,21 @@ private:
   const BackCert& subject;
   const Time time;
   const KeyPurposeId requiredEKUIfPresent;
   const CertPolicyId& requiredPolicy;
   /*optional*/ Input const* const stapledOCSPResponse;
   const unsigned int subCACount;
   const Result deferredSubjectError;
 
+  // Initialized lazily.
+  uint8_t subjectSignatureDigestBuf[MAX_DIGEST_SIZE_IN_BYTES];
+  der::PublicKeyAlgorithm subjectSignaturePublicKeyAlg;
+  SignedDigest subjectSignature;
+
   Result RecordResult(Result currentResult, /*out*/ bool& keepGoing);
   Result result;
   bool resultWasSet;
 
   PathBuildingStep(const PathBuildingStep&) = delete;
   void operator=(const PathBuildingStep&) = delete;
 };
 
@@ -187,18 +192,32 @@ PathBuildingStep::Check(Input potentialI
   // subject public key MUST NOT be used to verify signatures on certificates
   // or CRLs unless the corresponding keyCertSign or cRLSign bit is set."
   rv = BuildForward(trustDomain, potentialIssuer, time, KeyUsage::keyCertSign,
                     requiredEKUIfPresent, requiredPolicy, nullptr, subCACount);
   if (rv != Success) {
     return RecordResult(rv, keepGoing);
   }
 
-  rv = WrappedVerifySignedData(trustDomain, subject.GetSignedData(),
-                               potentialIssuer.GetSubjectPublicKeyInfo());
+  // Calculate the digest of the subject's signed data if we haven't already
+  // done so. We do this lazily to avoid doing it at all if we backtrack before
+  // getting to this point. We cache the result to avoid recalculating it if we
+  // backtrack after getting to this point.
+  if (subjectSignature.digest.GetLength() == 0) {
+    rv = DigestSignedData(trustDomain, subject.GetSignedData(),
+                          subjectSignatureDigestBuf,
+                          subjectSignaturePublicKeyAlg, subjectSignature);
+    if (rv != Success) {
+      return rv;
+    }
+  }
+
+  rv = VerifySignedDigest(trustDomain, subjectSignaturePublicKeyAlg,
+                          subjectSignature,
+                          potentialIssuer.GetSubjectPublicKeyInfo());
   if (rv != Success) {
     return RecordResult(rv, keepGoing);
   }
 
   // We avoid doing revocation checking for expired certificates because OCSP
   // responders are allowed to forget about expired certificates, and many OCSP
   // responders return an error when asked for the status of an expired
   // certificate.
--- a/security/pkix/lib/pkixcert.cpp
+++ b/security/pkix/lib/pkixcert.cpp
@@ -77,18 +77,17 @@ BackCert::Init()
   }
   rv = der::CertificateSerialNumber(tbsCertificate, serialNumber);
   if (rv != Success) {
     return rv;
   }
   // XXX: Ignored. What are we supposed to check? This seems totally redundant
   // with Certificate.signatureAlgorithm. Is it important to check that they
   // are consistent with each other? It doesn't seem to matter!
-  SignatureAlgorithm signature;
-  rv = der::SignatureAlgorithmIdentifier(tbsCertificate, signature);
+  rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, signature);
   if (rv != Success) {
     return rv;
   }
   rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer);
   if (rv != Success) {
     return rv;
   }
   rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity);
--- a/security/pkix/lib/pkixder.cpp
+++ b/security/pkix/lib/pkixder.cpp
@@ -90,58 +90,38 @@ OptionalNull(Reader& input)
     return Null(input);
   }
   return Success;
 }
 
 namespace {
 
 Result
-DigestAlgorithmOIDValue(Reader& algorithmID,
-                        /*out*/ DigestAlgorithm& algorithm)
+AlgorithmIdentifierValue(Reader& input, /*out*/ Reader& algorithmOIDValue)
 {
-  // RFC 4055 Section 2.1
-  // python DottedOIDToCode.py id-sha1 1.3.14.3.2.26
-  static const uint8_t id_sha1[] = {
-    0x2b, 0x0e, 0x03, 0x02, 0x1a
-  };
-  // python DottedOIDToCode.py id-sha256 2.16.840.1.101.3.4.2.1
-  static const uint8_t id_sha256[] = {
-    0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
-  };
-  // python DottedOIDToCode.py id-sha384 2.16.840.1.101.3.4.2.2
-  static const uint8_t id_sha384[] = {
-    0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
-  };
-  // python DottedOIDToCode.py id-sha512 2.16.840.1.101.3.4.2.3
-  static const uint8_t id_sha512[] = {
-    0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
-  };
+  Result rv = ExpectTagAndGetValue(input, der::OIDTag, algorithmOIDValue);
+  if (rv != Success) {
+    return rv;
+  }
+  return OptionalNull(input);
+}
+
+} // unnamed namespace
 
-  // Matching is attempted based on a rough estimate of the commonality of the
-  // algorithm, to minimize the number of MatchRest calls.
-  if (algorithmID.MatchRest(id_sha1)) {
-    algorithm = DigestAlgorithm::sha1;
-  } else if (algorithmID.MatchRest(id_sha256)) {
-    algorithm = DigestAlgorithm::sha256;
-  } else if (algorithmID.MatchRest(id_sha384)) {
-    algorithm = DigestAlgorithm::sha384;
-  } else if (algorithmID.MatchRest(id_sha512)) {
-    algorithm = DigestAlgorithm::sha512;
-  } else {
-    return Result::ERROR_INVALID_ALGORITHM;
+Result
+SignatureAlgorithmIdentifierValue(Reader& input,
+                                 /*out*/ PublicKeyAlgorithm& publicKeyAlgorithm,
+                                 /*out*/ DigestAlgorithm& digestAlgorithm)
+{
+  Reader algorithmID;
+  Result rv = AlgorithmIdentifierValue(input, algorithmID);
+  if (rv != Success) {
+    return rv;
   }
 
-  return Success;
-}
-
-Result
-SignatureAlgorithmOIDValue(Reader& algorithmID,
-                           /*out*/ SignatureAlgorithm& algorithm)
-{
   // RFC 5758 Section 3.2 (ecdsa-with-SHA224 is intentionally excluded)
   // python DottedOIDToCode.py ecdsa-with-SHA256 1.2.840.10045.4.3.2
   static const uint8_t ecdsa_with_SHA256[] = {
     0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02
   };
   // python DottedOIDToCode.py ecdsa-with-SHA384 1.2.840.10045.4.3.3
   static const uint8_t ecdsa_with_SHA384[] = {
     0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03
@@ -193,83 +173,95 @@ SignatureAlgorithmOIDValue(Reader& algor
   // RFC 4055 Section 5 and RFC 3279 Section 2.2.1 both say that parameters for
   // RSA must be encoded as NULL; we relax that requirement by allowing the
   // NULL to be omitted, to match all the other signature algorithms we support
   // and for compatibility.
 
   // Matching is attempted based on a rough estimate of the commonality of the
   // algorithm, to minimize the number of MatchRest calls.
   if (algorithmID.MatchRest(sha256WithRSAEncryption)) {
-    algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha256;
+    publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
+    digestAlgorithm = DigestAlgorithm::sha256;
   } else if (algorithmID.MatchRest(ecdsa_with_SHA256)) {
-    algorithm = SignatureAlgorithm::ecdsa_with_sha256;
+    publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
+    digestAlgorithm = DigestAlgorithm::sha256;
   } else if (algorithmID.MatchRest(sha_1WithRSAEncryption)) {
-    algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha1;
+    publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
+    digestAlgorithm = DigestAlgorithm::sha1;
   } else if (algorithmID.MatchRest(ecdsa_with_SHA1)) {
-    algorithm = SignatureAlgorithm::ecdsa_with_sha1;
+    publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
+    digestAlgorithm = DigestAlgorithm::sha1;
   } else if (algorithmID.MatchRest(ecdsa_with_SHA384)) {
-    algorithm = SignatureAlgorithm::ecdsa_with_sha384;
+    publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
+    digestAlgorithm = DigestAlgorithm::sha384;
   } else if (algorithmID.MatchRest(ecdsa_with_SHA512)) {
-    algorithm = SignatureAlgorithm::ecdsa_with_sha512;
+    publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
+    digestAlgorithm = DigestAlgorithm::sha512;
   } else if (algorithmID.MatchRest(sha384WithRSAEncryption)) {
-    algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha384;
+    publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
+    digestAlgorithm = DigestAlgorithm::sha384;
   } else if (algorithmID.MatchRest(sha512WithRSAEncryption)) {
-    algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha512;
+    publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
+    digestAlgorithm = DigestAlgorithm::sha512;
   } else if (algorithmID.MatchRest(sha1WithRSASignature)) {
     // XXX(bug 1042479): recognize this old OID for compatibility.
-    algorithm = SignatureAlgorithm::rsa_pkcs1_with_sha1;
+    publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
+    digestAlgorithm = DigestAlgorithm::sha1;
   } else {
-    algorithm = SignatureAlgorithm::unsupported_algorithm;
+    return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
   }
 
   return Success;
 }
 
-template <typename OidValueParser, typename Algorithm>
-Result
-AlgorithmIdentifier(OidValueParser oidValueParser, Reader& input,
-                    /*out*/ Algorithm& algorithm)
-{
-  Reader value;
-  Result rv = ExpectTagAndGetValue(input, SEQUENCE, value);
-  if (rv != Success) {
-    return rv;
-  }
-
-  Reader algorithmID;
-  rv = ExpectTagAndGetValue(value, der::OIDTag, algorithmID);
-  if (rv != Success) {
-    return rv;
-  }
-  rv = oidValueParser(algorithmID, algorithm);
-  if (rv != Success) {
-    return rv;
-  }
-
-  rv = OptionalNull(value);
-  if (rv != Success) {
-    return rv;
-  }
-
-  return End(value);
-}
-
-} // unnamed namespace
-
-Result
-SignatureAlgorithmIdentifier(Reader& input,
-                             /*out*/ SignatureAlgorithm& algorithm)
-{
-  return AlgorithmIdentifier(SignatureAlgorithmOIDValue, input, algorithm);
-}
-
 Result
 DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm)
 {
-  return AlgorithmIdentifier(DigestAlgorithmOIDValue, input, algorithm);
+  Reader r;
+  return der::Nested(input, SEQUENCE, [&algorithm](Reader& r) -> Result {
+    Reader algorithmID;
+    Result rv = AlgorithmIdentifierValue(r, algorithmID);
+    if (rv != Success) {
+      return rv;
+    }
+
+    // RFC 4055 Section 2.1
+    // python DottedOIDToCode.py id-sha1 1.3.14.3.2.26
+    static const uint8_t id_sha1[] = {
+      0x2b, 0x0e, 0x03, 0x02, 0x1a
+    };
+    // python DottedOIDToCode.py id-sha256 2.16.840.1.101.3.4.2.1
+    static const uint8_t id_sha256[] = {
+      0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
+    };
+    // python DottedOIDToCode.py id-sha384 2.16.840.1.101.3.4.2.2
+    static const uint8_t id_sha384[] = {
+      0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
+    };
+    // python DottedOIDToCode.py id-sha512 2.16.840.1.101.3.4.2.3
+    static const uint8_t id_sha512[] = {
+      0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
+    };
+
+    // Matching is attempted based on a rough estimate of the commonality of the
+    // algorithm, to minimize the number of MatchRest calls.
+    if (algorithmID.MatchRest(id_sha1)) {
+      algorithm = DigestAlgorithm::sha1;
+    } else if (algorithmID.MatchRest(id_sha256)) {
+      algorithm = DigestAlgorithm::sha256;
+    } else if (algorithmID.MatchRest(id_sha384)) {
+      algorithm = DigestAlgorithm::sha384;
+    } else if (algorithmID.MatchRest(id_sha512)) {
+      algorithm = DigestAlgorithm::sha512;
+    } else {
+      return Result::ERROR_INVALID_ALGORITHM;
+    }
+
+    return Success;
+  });
 }
 
 Result
 SignedData(Reader& input, /*out*/ Reader& tbs,
            /*out*/ SignedDataWithSignature& signedData)
 {
   Reader::Mark mark(input.GetMark());
 
@@ -279,17 +271,17 @@ SignedData(Reader& input, /*out*/ Reader
     return rv;
   }
 
   rv = input.GetInput(mark, signedData.data);
   if (rv != Success) {
     return rv;
   }
 
-  rv = SignatureAlgorithmIdentifier(input, signedData.algorithm);
+  rv = ExpectTagAndGetValue(input, der::SEQUENCE, signedData.algorithm);
   if (rv != Success) {
     return rv;
   }
 
   rv = BitStringWithNoUnusedBits(input, signedData.signature);
   if (rv == Result::ERROR_BAD_DER) {
     rv = Result::ERROR_BAD_SIGNATURE;
   }
--- a/security/pkix/lib/pkixder.h
+++ b/security/pkix/lib/pkixder.h
@@ -595,25 +595,45 @@ OptionalExtensions(Reader& input, uint8_
   }
 
   return Success;
 }
 
 Result DigestAlgorithmIdentifier(Reader& input,
                                  /*out*/ DigestAlgorithm& algorithm);
 
-Result SignatureAlgorithmIdentifier(Reader& input,
-                                    /*out*/ SignatureAlgorithm& algorithm);
+enum PublicKeyAlgorithm
+{
+  RSA_PKCS1,
+  ECDSA,
+};
+
+Result SignatureAlgorithmIdentifierValue(
+         Reader& input,
+         /*out*/ PublicKeyAlgorithm& publicKeyAlgorithm,
+         /*out*/ DigestAlgorithm& digestAlgorithm);
+
+struct SignedDataWithSignature final
+{
+public:
+  Input data;
+  Input algorithm;
+  Input signature;
+
+  void operator=(const SignedDataWithSignature&) = delete;
+};
 
 // Parses a SEQUENCE into tbs and then parses an AlgorithmIdentifier followed
 // by a BIT STRING into signedData. This handles the commonality between
 // parsing the signed/signature fields of certificates and OCSP responses. In
 // the case of an OCSP response, the caller needs to parse the certs
 // separately.
 //
+// Note that signatureAlgorithm is NOT parsed or validated.
+//
 // Certificate  ::=  SEQUENCE  {
 //        tbsCertificate       TBSCertificate,
 //        signatureAlgorithm   AlgorithmIdentifier,
 //        signatureValue       BIT STRING  }
 //
 // BasicOCSPResponse       ::= SEQUENCE {
 //    tbsResponseData      ResponseData,
 //    signatureAlgorithm   AlgorithmIdentifier,
--- a/security/pkix/lib/pkixnss.cpp
+++ b/security/pkix/lib/pkixnss.cpp
@@ -32,110 +32,105 @@
 #include "pkix/pkix.h"
 #include "pkix/ScopedPtr.h"
 #include "pkixutil.h"
 #include "secerr.h"
 #include "sslerr.h"
 
 namespace mozilla { namespace pkix {
 
+namespace {
+
 Result
-VerifySignedDataNSS(const SignedDataWithSignature& sd,
-                    Input subjectPublicKeyInfo, void* pkcs11PinArg)
+VerifySignedDigest(const SignedDigest& sd,
+                   Input subjectPublicKeyInfo,
+                   SECOidTag pubKeyAlg,
+                   void* pkcs11PinArg)
 {
-  SECOidTag pubKeyAlg;
   SECOidTag digestAlg;
-  switch (sd.algorithm) {
-    case SignatureAlgorithm::ecdsa_with_sha512:
-      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
-      digestAlg = SEC_OID_SHA512;
-      break;
-    case SignatureAlgorithm::ecdsa_with_sha384:
-      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
-      digestAlg = SEC_OID_SHA384;
-      break;
-    case SignatureAlgorithm::ecdsa_with_sha256:
-      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
-      digestAlg = SEC_OID_SHA256;
-      break;
-    case SignatureAlgorithm::ecdsa_with_sha1:
-      pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
-      digestAlg = SEC_OID_SHA1;
-      break;
-    case SignatureAlgorithm::rsa_pkcs1_with_sha512:
-      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
-      digestAlg = SEC_OID_SHA512;
-      break;
-    case SignatureAlgorithm::rsa_pkcs1_with_sha384:
-      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
-      digestAlg = SEC_OID_SHA384;
-      break;
-    case SignatureAlgorithm::rsa_pkcs1_with_sha256:
-      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
-      digestAlg = SEC_OID_SHA256;
-      break;
-    case SignatureAlgorithm::rsa_pkcs1_with_sha1:
-      pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION;
-      digestAlg = SEC_OID_SHA1;
-      break;
-    case SignatureAlgorithm::unsupported_algorithm: // fall through
-      return NotReached("unknown signature algorithm",
-                        Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+  switch (sd.digestAlgorithm) {
+    case DigestAlgorithm::sha512: digestAlg = SEC_OID_SHA512; break;
+    case DigestAlgorithm::sha384: digestAlg = SEC_OID_SHA384; break;
+    case DigestAlgorithm::sha256: digestAlg = SEC_OID_SHA256; break;
+    case DigestAlgorithm::sha1: digestAlg = SEC_OID_SHA1; break;
     MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
   }
 
   SECItem subjectPublicKeyInfoSECItem =
     UnsafeMapInputToSECItem(subjectPublicKeyInfo);
   ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
     spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfoSECItem));
   if (!spki) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
   ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
     pubKey(SECKEY_ExtractPublicKey(spki.get()));
   if (!pubKey) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
 
-  // The static_cast is safe as long as the length of the data in sd.data can
-  // fit in an int. Right now that length is stored as a uint16_t, so this
-  // works. In the future this may change, hence the assertion.
-  // See also bug 921585.
-  static_assert(sizeof(decltype(sd.data.GetLength())) < sizeof(int),
-                "sd.data.GetLength() must fit in an int");
-  SECItem dataSECItem(UnsafeMapInputToSECItem(sd.data));
+  SECItem digestSECItem(UnsafeMapInputToSECItem(sd.digest));
   SECItem signatureSECItem(UnsafeMapInputToSECItem(sd.signature));
-  SECStatus srv = VFY_VerifyDataDirect(dataSECItem.data,
-                                       static_cast<int>(dataSECItem.len),
-                                       pubKey.get(), &signatureSECItem,
-                                       pubKeyAlg, digestAlg, nullptr,
-                                       pkcs11PinArg);
+  SECStatus srv = VFY_VerifyDigestDirect(&digestSECItem, pubKey.get(),
+                                         &signatureSECItem, pubKeyAlg,
+                                         digestAlg, pkcs11PinArg);
   if (srv != SECSuccess) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
 
   return Success;
 }
 
+} // unnamed namespace
+
 Result
-DigestBufNSS(Input item, /*out*/ uint8_t* digestBuf, size_t digestBufLen)
+VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd,
+                              Input subjectPublicKeyInfo,
+                              void* pkcs11PinArg)
+{
+  return VerifySignedDigest(sd, subjectPublicKeyInfo,
+                            SEC_OID_PKCS1_RSA_ENCRYPTION, pkcs11PinArg);
+}
+
+Result
+VerifyECDSASignedDigestNSS(const SignedDigest& sd,
+                           Input subjectPublicKeyInfo,
+                           void* pkcs11PinArg)
 {
-  static_assert(TrustDomain::DIGEST_LENGTH == SHA1_LENGTH,
-                "TrustDomain::DIGEST_LENGTH must be 20 (SHA-1 digest length)");
-  if (digestBufLen != TrustDomain::DIGEST_LENGTH) {
-    return NotReached("invalid hash length", Result::FATAL_ERROR_INVALID_ARGS);
+  return VerifySignedDigest(sd, subjectPublicKeyInfo,
+                            SEC_OID_ANSIX962_EC_PUBLIC_KEY, pkcs11PinArg);
+}
+
+Result
+DigestBufNSS(Input item,
+             DigestAlgorithm digestAlg,
+             /*out*/ uint8_t* digestBuf,
+             size_t digestBufLen)
+{
+  SECOidTag oid;
+  size_t bits;
+  switch (digestAlg) {
+    case DigestAlgorithm::sha512: oid = SEC_OID_SHA512; bits = 512; break;
+    case DigestAlgorithm::sha384: oid = SEC_OID_SHA384; bits = 384; break;
+    case DigestAlgorithm::sha256: oid = SEC_OID_SHA256; bits = 256; break;
+    case DigestAlgorithm::sha1: oid = SEC_OID_SHA1; bits = 160; break;
+    MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
   }
+  if (digestBufLen != bits / 8) {
+    return Result::FATAL_ERROR_INVALID_ARGS;
+  }
+
   SECItem itemSECItem = UnsafeMapInputToSECItem(item);
   if (itemSECItem.len >
         static_cast<decltype(itemSECItem.len)>(
           std::numeric_limits<int32_t>::max())) {
     PR_NOT_REACHED("large items should not be possible here");
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
-  SECStatus srv = PK11_HashBuf(SEC_OID_SHA1, digestBuf, itemSECItem.data,
+  SECStatus srv = PK11_HashBuf(oid, digestBuf, itemSECItem.data,
                                static_cast<int32_t>(itemSECItem.len));
   if (srv != SECSuccess) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
   return Success;
 }
 
 Result
--- a/security/pkix/lib/pkixocsp.cpp
+++ b/security/pkix/lib/pkixocsp.cpp
@@ -22,17 +22,22 @@
  * limitations under the License.
  */
 
 #include <limits>
 
 #include "pkix/pkix.h"
 #include "pkixcheck.h"
 #include "pkixutil.h"
-#include "pkixder.h"
+
+namespace {
+
+const size_t SHA1_DIGEST_LENGTH = 160 / 8;
+
+} // unnamed namespace
 
 namespace mozilla { namespace pkix {
 
 // These values correspond to the tag values in the ASN.1 CertStatus
 enum class CertStatus : uint8_t {
   Good = der::CONTEXT_SPECIFIC | 0,
   Revoked = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
   Unknown = der::CONTEXT_SPECIFIC | 2
@@ -123,18 +128,19 @@ CheckOCSPResponseSignerCert(TrustDomain&
   // XXX(bug 926270) XXX(bug 1008133) XXX(bug 980163): Improve name
   // comparison.
   // TODO: needs test
   if (!InputsAreEqual(potentialSigner.GetIssuer(), issuerSubject)) {
     return Result::ERROR_OCSP_RESPONDER_CERT_INVALID;
   }
 
   // TODO(bug 926260): check name constraints
-  rv = WrappedVerifySignedData(trustDomain, potentialSigner.GetSignedData(),
-                               issuerSubjectPublicKeyInfo);
+
+  rv = VerifySignedData(trustDomain, potentialSigner.GetSignedData(),
+                        issuerSubjectPublicKeyInfo);
 
   // TODO: check for revocation of the OCSP responder certificate unless no-check
   // or the caller forcing no-check. To properly support the no-check policy, we'd
   // need to enforce policy constraints from the issuerChain.
 
   return rv;
 }
 
@@ -145,17 +151,17 @@ enum class ResponderIDType : uint8_t
 };
 
 static inline Result OCSPResponse(Reader&, Context&);
 static inline Result ResponseBytes(Reader&, Context&);
 static inline Result BasicResponse(Reader&, Context&);
 static inline Result ResponseData(
                        Reader& tbsResponseData,
                        Context& context,
-                       const SignedDataWithSignature& signedResponseData,
+                       const der::SignedDataWithSignature& signedResponseData,
                        const DERArray& certs);
 static inline Result SingleResponse(Reader& input, Context& context);
 static Result ExtensionNotUnderstood(Reader& extnID, Input extnValue,
                                      bool critical, /*out*/ bool& understood);
 static inline Result CertID(Reader& input,
                             const Context& context,
                             /*out*/ bool& match);
 static Result MatchKeyHash(TrustDomain& trustDomain,
@@ -196,36 +202,36 @@ MatchResponderID(TrustDomain& trustDomai
     }
 
     MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
   }
 }
 
 static Result
 VerifyOCSPSignedData(TrustDomain& trustDomain,
-                     const SignedDataWithSignature& signedResponseData,
+                     const der::SignedDataWithSignature& signedResponseData,
                      Input spki)
 {
-  Result rv = WrappedVerifySignedData(trustDomain, signedResponseData, spki);
+  Result rv = VerifySignedData(trustDomain, signedResponseData, spki);
   if (rv == Result::ERROR_BAD_SIGNATURE) {
     rv = Result::ERROR_OCSP_BAD_SIGNATURE;
   }
   return rv;
 }
 
 // RFC 6960 section 4.2.2.2: The OCSP responder must either be the issuer of
 // the cert or it must be a delegated OCSP response signing cert directly
 // issued by the issuer. If the OCSP responder is a delegated OCSP response
 // signer, then its certificate is (probably) embedded within the OCSP
 // response and we'll need to verify that it is a valid certificate that chains
 // *directly* to issuerCert.
 static Result
 VerifySignature(Context& context, ResponderIDType responderIDType,
                 Input responderID, const DERArray& certs,
-                const SignedDataWithSignature& signedResponseData)
+                const der::SignedDataWithSignature& signedResponseData)
 {
   bool match;
   Result rv = MatchResponderID(context.trustDomain, responderIDType,
                                responderID, context.certID.issuer,
                                context.certID.issuerSubjectPublicKeyInfo,
                                match);
   if (rv != Success) {
     return rv;
@@ -386,17 +392,17 @@ ResponseBytes(Reader& input, Context& co
 //    tbsResponseData      ResponseData,
 //    signatureAlgorithm   AlgorithmIdentifier,
 //    signature            BIT STRING,
 //    certs            [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
 Result
 BasicResponse(Reader& input, Context& context)
 {
   Reader tbsResponseData;
-  SignedDataWithSignature signedData;
+  der::SignedDataWithSignature signedData;
   Result rv = der::SignedData(input, tbsResponseData, signedData);
   if (rv != Success) {
     if (rv == Result::ERROR_BAD_SIGNATURE) {
       return Result::ERROR_OCSP_BAD_SIGNATURE;
     }
     return rv;
   }
 
@@ -443,17 +449,17 @@ BasicResponse(Reader& input, Context& co
 // ResponseData ::= SEQUENCE {
 //    version             [0] EXPLICIT Version DEFAULT v1,
 //    responderID             ResponderID,
 //    producedAt              GeneralizedTime,
 //    responses               SEQUENCE OF SingleResponse,
 //    responseExtensions  [1] EXPLICIT Extensions OPTIONAL }
 static inline Result
 ResponseData(Reader& input, Context& context,
-             const SignedDataWithSignature& signedResponseData,
+             const der::SignedDataWithSignature& signedResponseData,
              const DERArray& certs)
 {
   der::Version version;
   Result rv = der::OptionalVersion(input, version);
   if (rv != Success) {
     return rv;
   }
   if (version != der::Version::v1) {
@@ -713,25 +719,26 @@ CertID(Reader& input, const Context& con
   // TODO: support SHA-2 hashes.
 
   if (hashAlgorithm != DigestAlgorithm::sha1) {
     // Again, not interested in this response. Consume input, return success.
     input.SkipToEnd();
     return Success;
   }
 
-  if (issuerNameHash.GetLength() != TrustDomain::DIGEST_LENGTH) {
+  if (issuerNameHash.GetLength() != SHA1_DIGEST_LENGTH) {
     return Result::ERROR_OCSP_MALFORMED_RESPONSE;
   }
 
   // From http://tools.ietf.org/html/rfc6960#section-4.1.1:
   // "The hash shall be calculated over the DER encoding of the
   // issuer's name field in the certificate being checked."
-  uint8_t hashBuf[TrustDomain::DIGEST_LENGTH];
-  rv = context.trustDomain.DigestBuf(context.certID.issuer, hashBuf,
+  uint8_t hashBuf[SHA1_DIGEST_LENGTH];
+  rv = context.trustDomain.DigestBuf(context.certID.issuer,
+                                     DigestAlgorithm::sha1, hashBuf,
                                      sizeof(hashBuf));
   if (rv != Success) {
     return rv;
   }
   Input computed(hashBuf);
   if (!InputsAreEqual(computed, issuerNameHash)) {
     // Again, not interested in this response. Consume input, return success.
     input.SkipToEnd();
@@ -751,36 +758,36 @@ CertID(Reader& input, const Context& con
 //                          -- (i.e., the SHA-1 hash of the value of the
 //                          -- BIT STRING subjectPublicKey [excluding
 //                          -- the tag, length, and number of unused
 //                          -- bits] in the responder's certificate)
 static Result
 MatchKeyHash(TrustDomain& trustDomain, Input keyHash,
              const Input subjectPublicKeyInfo, /*out*/ bool& match)
 {
-  if (keyHash.GetLength() != TrustDomain::DIGEST_LENGTH)  {
+  if (keyHash.GetLength() != SHA1_DIGEST_LENGTH)  {
     return Result::ERROR_OCSP_MALFORMED_RESPONSE;
   }
-  static uint8_t hashBuf[TrustDomain::DIGEST_LENGTH];
+  static uint8_t hashBuf[SHA1_DIGEST_LENGTH];
   Result rv = KeyHash(trustDomain, subjectPublicKeyInfo, hashBuf,
                       sizeof hashBuf);
   if (rv != Success) {
     return rv;
   }
   Input computed(hashBuf);
   match = InputsAreEqual(computed, keyHash);
   return Success;
 }
 
 // TODO(bug 966856): support SHA-2 hashes
 Result
 KeyHash(TrustDomain& trustDomain, const Input subjectPublicKeyInfo,
         /*out*/ uint8_t* hashBuf, size_t hashBufSize)
 {
-  if (!hashBuf || hashBufSize != TrustDomain::DIGEST_LENGTH) {
+  if (!hashBuf || hashBufSize != SHA1_DIGEST_LENGTH) {
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   // RFC 5280 Section 4.1
   //
   // SubjectPublicKeyInfo  ::=  SEQUENCE  {
   //    algorithm            AlgorithmIdentifier,
   //    subjectPublicKey     BIT STRING  }
@@ -803,17 +810,18 @@ KeyHash(TrustDomain& trustDomain, const 
   if (rv != Success) {
     return rv;
   }
   rv = der::End(spki);
   if (rv != Success) {
     return rv;
   }
 
-  return trustDomain.DigestBuf(subjectPublicKey, hashBuf, hashBufSize);
+  return trustDomain.DigestBuf(subjectPublicKey, DigestAlgorithm::sha1,
+                               hashBuf, hashBufSize);
 }
 
 Result
 ExtensionNotUnderstood(Reader& /*extnID*/, Input /*extnValue*/,
                        bool /*critical*/, /*out*/ bool& understood)
 {
   understood = false;
   return Success;
@@ -863,17 +871,17 @@ CreateEncodedOCSPRequest(TrustDomain& tr
   // Since we don't know whether the OCSP responder supports anything other
   // than SHA-1, we have no choice but to use SHA-1 for issuerNameHash and
   // issuerKeyHash.
   static const uint8_t hashAlgorithm[11] = {
     0x30, 0x09,                               // SEQUENCE
     0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, //   OBJECT IDENTIFIER id-sha1
     0x05, 0x00,                               //   NULL
   };
-  static const uint8_t hashLen = TrustDomain::DIGEST_LENGTH;
+  static const uint8_t hashLen = 160 / 8;
 
   static const unsigned int totalLenWithoutSerialNumberData
     = 2                             // OCSPRequest
     + 2                             //   tbsRequest
     + 2                             //     requestList
     + 2                             //       Request
     + 2                             //         reqCert (CertID)
     + sizeof(hashAlgorithm)         //           hashAlgorithm
@@ -908,17 +916,18 @@ CreateEncodedOCSPRequest(TrustDomain& tr
   // reqCert.hashAlgorithm
   for (size_t i = 0; i < sizeof(hashAlgorithm); ++i) {
     *d++ = hashAlgorithm[i];
   }
 
   // reqCert.issuerNameHash (OCTET STRING)
   *d++ = 0x04;
   *d++ = hashLen;
-  Result rv = trustDomain.DigestBuf(certID.issuer, d, hashLen);
+  Result rv = trustDomain.DigestBuf(certID.issuer, DigestAlgorithm::sha1, d,
+                                    hashLen);
   if (rv != Success) {
     return rv;
   }
   d += hashLen;
 
   // reqCert.issuerKeyHash (OCTET STRING)
   *d++ = 0x04;
   *d++ = hashLen;
--- a/security/pkix/lib/pkixutil.h
+++ b/security/pkix/lib/pkixutil.h
@@ -49,17 +49,19 @@ public:
     , childCert(childCert)
   {
   }
 
   Result Init();
 
   const Input GetDER() const { return der; }
   der::Version GetVersion() const { return version; }
-  const SignedDataWithSignature& GetSignedData() const { return signedData; }
+  const der::SignedDataWithSignature& GetSignedData() const {
+    return signedData;
+  }
   const Input GetIssuer() const { return issuer; }
   // XXX: "validity" is a horrible name for the structure that holds
   // notBefore & notAfter, but that is the name used in RFC 5280 and we use the
   // RFC 5280 names for everything.
   const Input GetValidity() const { return validity; }
   const Input GetSerialNumber() const { return serialNumber; }
   const Input GetSubject() const { return subject; }
   const Input GetSubjectPublicKeyInfo() const
@@ -102,36 +104,37 @@ public:
 private:
   const Input der;
 
 public:
   const EndEntityOrCA endEntityOrCA;
   BackCert const* const childCert;
 
 private:
-  der::Version version;
-
   // When parsing certificates in BackCert::Init, we don't accept empty
   // extensions. Consequently, we don't have to store a distinction between
   // empty extensions and extensions that weren't included. However, when
   // *processing* extensions, we distinguish between whether an extension was
   // included or not based on whetehr the GetXXX function for the extension
   // returns nullptr.
   static inline const Input* MaybeInput(const Input& item)
   {
     return item.GetLength() > 0 ? &item : nullptr;
   }
 
-  SignedDataWithSignature signedData;
+  der::SignedDataWithSignature signedData;
+
+  der::Version version;
+  Input serialNumber;
+  Input signature;
   Input issuer;
   // XXX: "validity" is a horrible name for the structure that holds
   // notBefore & notAfter, but that is the name used in RFC 5280 and we use the
   // RFC 5280 names for everything.
   Input validity;
-  Input serialNumber;
   Input subject;
   Input subjectPublicKeyInfo;
 
   Input authorityInfoAccess;
   Input basicConstraints;
   Input certificatePolicies;
   Input extKeyUsage;
   Input inhibitAnyPolicy;
@@ -192,29 +195,33 @@ DaysBeforeYear(unsigned int year)
 {
   assert(year <= 9999);
   return ((year - 1u) * 365u)
        + ((year - 1u) / 4u)    // leap years are every 4 years,
        - ((year - 1u) / 100u)  // except years divisible by 100,
        + ((year - 1u) / 400u); // except years divisible by 400.
 }
 
-// Ensures that we do not call the TrustDomain's VerifySignedData function if
-// the algorithm is unsupported.
-inline Result
-WrappedVerifySignedData(TrustDomain& trustDomain,
-                        const SignedDataWithSignature& signedData,
-                        Input subjectPublicKeyInfo)
-{
-  if (signedData.algorithm == SignatureAlgorithm::unsupported_algorithm) {
-    return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
-  }
+static const size_t MAX_DIGEST_SIZE_IN_BYTES = 512 / 8; // sha-512
+
+Result DigestSignedData(TrustDomain& trustDomain,
+                        const der::SignedDataWithSignature& signedData,
+                        /*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES],
+                        /*out*/ der::PublicKeyAlgorithm& publicKeyAlg,
+                        /*out*/ SignedDigest& signedDigest);
 
-  return trustDomain.VerifySignedData(signedData, subjectPublicKeyInfo);
-}
+Result VerifySignedDigest(TrustDomain& trustDomain,
+                          der::PublicKeyAlgorithm publicKeyAlg,
+                          const SignedDigest& signedDigest,
+                          Input signerSubjectPublicKeyInfo);
+
+// Combines DigestSignedData and VerifySignedDigest
+Result VerifySignedData(TrustDomain& trustDomain,
+                        const der::SignedDataWithSignature& signedData,
+                        Input signerSubjectPublicKeyInfo);
 
 // In a switch over an enum, sometimes some compilers are not satisfied that
 // all control flow paths have been considered unless there is a default case.
 // However, in our code, such a default case is almost always unreachable dead
 // code. That can be particularly problematic when the compiler wants the code
 // to choose a value, such as a return value, for the default case, but there's
 // no appropriate "impossible case" value to choose.
 //
new file mode 100644
--- /dev/null
+++ b/security/pkix/lib/pkixverify.cpp
@@ -0,0 +1,103 @@
+/* -*- 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 code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2015 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pkixutil.h"
+
+namespace mozilla { namespace pkix {
+
+Result
+DigestSignedData(TrustDomain& trustDomain,
+                 const der::SignedDataWithSignature& signedData,
+                 /*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES],
+                 /*out*/ der::PublicKeyAlgorithm& publicKeyAlg,
+                 /*out*/ SignedDigest& signedDigest)
+{
+  Reader signatureAlg(signedData.algorithm);
+  Result rv = der::SignatureAlgorithmIdentifierValue(
+                signatureAlg, publicKeyAlg, signedDigest.digestAlgorithm);
+  if (rv != Success) {
+    return rv;
+  }
+  if (!signatureAlg.AtEnd()) {
+    return Result::ERROR_BAD_DER;
+  }
+
+  size_t digestLen;
+  switch (signedDigest.digestAlgorithm) {
+    case DigestAlgorithm::sha512: digestLen = 512 / 8; break;
+    case DigestAlgorithm::sha384: digestLen = 384 / 8; break;
+    case DigestAlgorithm::sha256: digestLen = 256 / 8; break;
+    case DigestAlgorithm::sha1: digestLen = 160 / 8; break;
+    MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
+  }
+  assert(digestLen <= sizeof(digestBuf));
+
+  rv = trustDomain.DigestBuf(signedData.data, signedDigest.digestAlgorithm,
+                             digestBuf, digestLen);
+  if (rv != Success) {
+    return rv;
+  }
+  rv = signedDigest.digest.Init(digestBuf, digestLen);
+  if (rv != Success) {
+    return rv;
+  }
+
+  return signedDigest.signature.Init(signedData.signature);
+}
+
+Result
+VerifySignedDigest(TrustDomain& trustDomain,
+                   der::PublicKeyAlgorithm publicKeyAlg,
+                   const SignedDigest& signedDigest,
+                   Input signerSubjectPublicKeyInfo)
+{
+  switch (publicKeyAlg) {
+    case der::PublicKeyAlgorithm::ECDSA:
+      return trustDomain.VerifyECDSASignedDigest(signedDigest,
+                                                 signerSubjectPublicKeyInfo);
+    case der::PublicKeyAlgorithm::RSA_PKCS1:
+      return trustDomain.VerifyRSAPKCS1SignedDigest(signedDigest,
+                                                    signerSubjectPublicKeyInfo);
+    MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
+  }
+}
+
+Result
+VerifySignedData(TrustDomain& trustDomain,
+                 const der::SignedDataWithSignature& signedData,
+                 Input signerSubjectPublicKeyInfo)
+{
+  uint8_t digestBuf[MAX_DIGEST_SIZE_IN_BYTES];
+  der::PublicKeyAlgorithm publicKeyAlg;
+  SignedDigest signedDigest;
+  Result rv = DigestSignedData(trustDomain, signedData, digestBuf,
+                               publicKeyAlg, signedDigest);
+  if (rv != Success) {
+    return rv;
+  }
+  return VerifySignedDigest(trustDomain, publicKeyAlg, signedDigest,
+                            signerSubjectPublicKeyInfo);
+}
+
+} } // namespace mozilla::pkix
--- a/security/pkix/moz.build
+++ b/security/pkix/moz.build
@@ -9,16 +9,17 @@ SOURCES += [
     'lib/pkixcert.cpp',
     'lib/pkixcheck.cpp',
     'lib/pkixder.cpp',
     'lib/pkixnames.cpp',
     'lib/pkixnss.cpp',
     'lib/pkixocsp.cpp',
     'lib/pkixresult.cpp',
     'lib/pkixtime.cpp',
+    'lib/pkixverify.cpp',
 ]
 
 LOCAL_INCLUDES += [
     'include',
 ]
 
 TEST_DIRS += [
     'test/gtest',
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp
+++ b/security/pkix/test/gtest/pkixbuild_tests.cpp
@@ -148,39 +148,45 @@ private:
     return Success;
   }
 
   Result IsChainValid(const DERArray&, Time) override
   {
     return Success;
   }
 
-  Result VerifySignedData(const SignedDataWithSignature& signedData,
-                          Input subjectPublicKeyInfo) override
+  Result DigestBuf(Input input, DigestAlgorithm digestAlg,
+                   /*out*/ uint8_t* digestBuf, size_t digestLen) override
   {
-    return TestVerifySignedData(signedData, subjectPublicKeyInfo);
-  }
-
-  Result DigestBuf(Input, /*out*/ uint8_t*, size_t) override
-  {
-    ADD_FAILURE();
-    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+    return TestDigestBuf(input, digestAlg, digestBuf, digestLen);
   }
 
   Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
                                             override
   {
     return Success;
   }
 
+  Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+                                    Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
   Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
   {
     return Success;
   }
 
+  Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                                 Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
 
   std::map<ByteString, ByteString> subjectDERToCertDER;
   ByteString leafCACertDER;
   ByteString rootCACertDER;
 };
 
 class pkixbuild : public ::testing::Test
 {
@@ -317,39 +323,45 @@ public:
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   Result IsChainValid(const DERArray&, Time) override
   {
     return Success;
   }
 
-  Result VerifySignedData(const SignedDataWithSignature& signedData,
-                          Input subjectPublicKeyInfo) override
+  Result DigestBuf(Input input, DigestAlgorithm digestAlg,
+                   /*out*/ uint8_t* digestBuf, size_t digestLen) override
   {
-    return TestVerifySignedData(signedData, subjectPublicKeyInfo);
-  }
-
-  Result DigestBuf(Input, /*out*/uint8_t*, size_t) override
-  {
-    ADD_FAILURE();
-    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+    return TestDigestBuf(input, digestAlg, digestBuf, digestLen);
   }
 
   Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
                                             override
   {
     return Success;
   }
 
+  Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+                                    Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
   Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
   {
     return Success;
   }
 
+  Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                                    Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
 private:
   ByteString rootDER;
 };
 
 TEST_F(pkixbuild, NoRevocationCheckingForExpiredCert)
 {
   const char* rootCN = "Root CA";
   ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA,
@@ -406,37 +418,45 @@ public:
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   Result IsChainValid(const DERArray&, Time) override
   {
     return Success;
   }
 
-  Result VerifySignedData(const SignedDataWithSignature&, Input) override
-  {
-    ADD_FAILURE();
-    return Result::FATAL_ERROR_LIBRARY_FAILURE;
-  }
-
-  Result DigestBuf(Input, /*out*/uint8_t*, size_t) override
+  Result DigestBuf(Input, DigestAlgorithm, /*out*/uint8_t*, size_t) override
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
                                             override
   {
-    return Success;
+    ADD_FAILURE();
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+  }
+
+  Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override
+  {
+    ADD_FAILURE();
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
   {
-    return Success;
+    ADD_FAILURE();
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+  }
+
+  Result VerifyECDSASignedDigest(const SignedDigest&, Input) override
+  {
+    ADD_FAILURE();
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 };
 
 class pkixbuild_DSS : public ::testing::Test { };
 
 TEST_F(pkixbuild_DSS, DSSEndEntityKeyNotAccepted)
 {
   DSSTrustDomain trustDomain;
@@ -509,39 +529,45 @@ public:
     return Success;
   }
 
   Result IsChainValid(const DERArray&, Time) override
   {
     return Success;
   }
 
-  Result VerifySignedData(const SignedDataWithSignature& signedData,
-                          Input subjectPublicKeyInfo) override
+  Result DigestBuf(Input input, DigestAlgorithm digestAlg,
+                   /*out*/ uint8_t* digestBuf, size_t digestLen) override
   {
-    return TestVerifySignedData(signedData, subjectPublicKeyInfo);
-  }
-
-  Result DigestBuf(Input, /*out*/uint8_t*, size_t) override
-  {
-    ADD_FAILURE();
-    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+    return TestDigestBuf(input, digestAlg, digestBuf, digestLen);
   }
 
   Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
                                             override
   {
     return Success;
   }
 
+  Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+                                    Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
   Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
   {
     return Success;
   }
 
+  Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                                    Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
 private:
   const ByteString issuer;
   const bool expectedKeepGoing;
 };
 
 struct IssuerNameCheckParams
 {
   const char* subjectIssuerCN; // null means "empty name"
--- a/security/pkix/test/gtest/pkixcert_extension_tests.cpp
+++ b/security/pkix/test/gtest/pkixcert_extension_tests.cpp
@@ -84,38 +84,45 @@ private:
     return Success;
   }
 
   Result IsChainValid(const DERArray&, Time) override
   {
     return Success;
   }
 
-  Result VerifySignedData(const SignedDataWithSignature& signedData,
-                          Input subjectPublicKeyInfo) override
+  Result DigestBuf(Input input, DigestAlgorithm digestAlg,
+                   /*out*/ uint8_t* digestBuf, size_t digestLen) override
   {
-    return TestVerifySignedData(signedData, subjectPublicKeyInfo);
-  }
-
-  Result DigestBuf(Input, /*out*/ uint8_t*, size_t) override
-  {
-    ADD_FAILURE();
-    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+    return TestDigestBuf(input, digestAlg, digestBuf, digestLen);
   }
 
   Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
                                             override
   {
     return Success;
   }
 
+  Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+                                    Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
   Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
   {
     return Success;
   }
+
+  Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                                 Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
 };
 
 // python DottedOIDToCode.py --tlv unknownExtensionOID 1.3.6.1.4.1.13769.666.666.666.1.500.9.3
 static const uint8_t tlv_unknownExtensionOID[] = {
   0x06, 0x12, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a, 0x85, 0x1a,
   0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03
 };
 
--- a/security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp
+++ b/security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp
@@ -98,40 +98,45 @@ private:
     return Success;
   }
 
   Result IsChainValid(const DERArray&, Time) override
   {
     return Success;
   }
 
-  Result VerifySignedData(const SignedDataWithSignature& signedData,
-                          Input subjectPublicKeyInfo) override
+  Result DigestBuf(Input input, DigestAlgorithm digestAlg,
+                   /*out*/ uint8_t* digestBuf, size_t digestLen) override
   {
-    EXPECT_NE(SignatureAlgorithm::unsupported_algorithm, signedData.algorithm);
-    return TestVerifySignedData(signedData, subjectPublicKeyInfo);
-  }
-
-  Result DigestBuf(Input, uint8_t*, size_t) override
-  {
-    ADD_FAILURE();
-    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+    return TestDigestBuf(input, digestAlg, digestBuf, digestLen);
   }
 
   Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
                                             override
   {
     return Success;
   }
 
+  Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+                                    Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
   Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
   {
     return Success;
   }
 
+  Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                                 Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
   ByteString rootDER;
   ByteString rootSubjectDER;
   ByteString intDER;
   ByteString intSubjectDER;
 };
 
 static const ByteString NO_INTERMEDIATE; // empty
 
--- a/security/pkix/test/gtest/pkixder_pki_types_tests.cpp
+++ b/security/pkix/test/gtest/pkixder_pki_types_tests.cpp
@@ -185,33 +185,38 @@ TEST_F(pkixder_pki_types_tests, Optional
 
   der::Version version = der::Version::v3;
   ASSERT_EQ(Success, OptionalVersion(reader, version));
   ASSERT_EQ(der::Version::v1, version);
 }
 
 static const size_t MAX_ALGORITHM_OID_DER_LENGTH = 13;
 
-template <typename T>
-struct AlgorithmIdentifierTestInfo
+struct InvalidAlgorithmIdentifierTestInfo
 {
-  T algorithm;
   uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH];
   size_t derLength;
 };
 
-class pkixder_DigestAlgorithmIdentifier
+struct ValidDigestAlgorithmIdentifierTestInfo
+{
+  DigestAlgorithm algorithm;
+  uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH];
+  size_t derLength;
+};
+
+class pkixder_DigestAlgorithmIdentifier_Valid
   : public ::testing::Test
-  , public ::testing::WithParamInterface<
-                AlgorithmIdentifierTestInfo<DigestAlgorithm>>
+  , public ::testing::WithParamInterface<ValidDigestAlgorithmIdentifierTestInfo>
 {
 };
 
-static const AlgorithmIdentifierTestInfo<DigestAlgorithm>
-DIGEST_ALGORITHM_TEST_INFO[] = {
+static const ValidDigestAlgorithmIdentifierTestInfo
+  VALID_DIGEST_ALGORITHM_TEST_INFO[] =
+{
   { DigestAlgorithm::sha512,
     { 0x30, 0x0b, 0x06, 0x09,
       0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 },
     13
   },
   { DigestAlgorithm::sha384,
     { 0x30, 0x0b, 0x06, 0x09,
       0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 },
@@ -224,19 +229,19 @@ DIGEST_ALGORITHM_TEST_INFO[] = {
   },
   { DigestAlgorithm::sha1,
     { 0x30, 0x07, 0x06, 0x05,
       0x2b, 0x0e, 0x03, 0x02, 0x1a },
     9
   },
 };
 
-TEST_P(pkixder_DigestAlgorithmIdentifier, Valid)
+TEST_P(pkixder_DigestAlgorithmIdentifier_Valid, Valid)
 {
-  const AlgorithmIdentifierTestInfo<DigestAlgorithm>& param(GetParam());
+  const ValidDigestAlgorithmIdentifierTestInfo& param(GetParam());
 
   {
     Input input;
     ASSERT_EQ(Success, input.Init(param.der, param.derLength));
     Reader reader(input);
     DigestAlgorithm alg;
     ASSERT_EQ(Success, DigestAlgorithmIdentifier(reader, alg));
     ASSERT_EQ(param.algorithm, alg);
@@ -255,170 +260,223 @@ TEST_P(pkixder_DigestAlgorithmIdentifier
     Reader reader(input);
     DigestAlgorithm alg;
     ASSERT_EQ(Success, DigestAlgorithmIdentifier(reader, alg));
     ASSERT_EQ(param.algorithm, alg);
     ASSERT_EQ(Success, End(reader));
   }
 }
 
-INSTANTIATE_TEST_CASE_P(pkixder_DigestAlgorithmIdentifier,
-                        pkixder_DigestAlgorithmIdentifier,
-                        testing::ValuesIn(DIGEST_ALGORITHM_TEST_INFO));
+INSTANTIATE_TEST_CASE_P(pkixder_DigestAlgorithmIdentifier_Valid,
+                        pkixder_DigestAlgorithmIdentifier_Valid,
+                        testing::ValuesIn(VALID_DIGEST_ALGORITHM_TEST_INFO));
 
-TEST_F(pkixder_DigestAlgorithmIdentifier, Invalid_MD5)
+class pkixder_DigestAlgorithmIdentifier_Invalid
+  : public ::testing::Test
+  , public ::testing::WithParamInterface<InvalidAlgorithmIdentifierTestInfo>
 {
-  // The OID identifies MD5 (1.2.840.113549.2.5). It is invalid because we
-  // don't accept MD5 as a hash algorithm.
-  static const uint8_t DER[] = {
-    0x30, 0x0a, 0x06, 0x08,
-    0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05
-  };
-  Input input(DER);
-  Reader reader(input);
+};
 
-  DigestAlgorithm alg;
-  ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM,
-            DigestAlgorithmIdentifier(reader, alg));
-}
-
-TEST_F(pkixder_DigestAlgorithmIdentifier, Invalid_Digest_ECDSA_WITH_SHA256)
+static const InvalidAlgorithmIdentifierTestInfo
+  INVALID_DIGEST_ALGORITHM_TEST_INFO[] =
 {
-  // The OID identifies ecdsa-with-SHA256 (1.2.840.10045.4.3.2). It is invalid
-  // because ECDSA-with-SHA256 is not a hash algorithm.
-  static const uint8_t DER[] = {
-    0x30, 0x0a, 0x06, 0x08,
-    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, //
-  };
-  Input input(DER);
+  { // MD5
+    { 0x30, 0x0a, 0x06, 0x08,
+      0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05 },
+    12,
+  },
+  { // ecdsa-with-SHA256 (1.2.840.10045.4.3.2) (not a hash algorithm)
+    { 0x30, 0x0a, 0x06, 0x08,
+      0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 },
+    12,
+  },
+};
+
+TEST_P(pkixder_DigestAlgorithmIdentifier_Invalid, Invalid)
+{
+  const InvalidAlgorithmIdentifierTestInfo& param(GetParam());
+  Input input;
+  ASSERT_EQ(Success, input.Init(param.der, param.derLength));
   Reader reader(input);
-
   DigestAlgorithm alg;
   ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM,
             DigestAlgorithmIdentifier(reader, alg));
 }
 
-static const AlgorithmIdentifierTestInfo<SignatureAlgorithm>
-  SIGNATURE_ALGORITHM_TEST_INFO[] =
+INSTANTIATE_TEST_CASE_P(pkixder_DigestAlgorithmIdentifier_Invalid,
+                        pkixder_DigestAlgorithmIdentifier_Invalid,
+                        testing::ValuesIn(INVALID_DIGEST_ALGORITHM_TEST_INFO));
+
+struct ValidSignatureAlgorithmIdentifierValueTestInfo
 {
-  { SignatureAlgorithm::ecdsa_with_sha512,
-    { 0x30, 0x0a, 0x06, 0x08,
+  PublicKeyAlgorithm publicKeyAlg;
+  DigestAlgorithm digestAlg;
+  uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH];
+  size_t derLength;
+};
+
+static const ValidSignatureAlgorithmIdentifierValueTestInfo
+  VALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO[] =
+{
+  // ECDSA
+  { PublicKeyAlgorithm::ECDSA,
+    DigestAlgorithm::sha512,
+    { 0x06, 0x08,
       0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 },
-    12,
+    10,
   },
-  { SignatureAlgorithm::ecdsa_with_sha384,
-    { 0x30, 0x0a, 0x06, 0x08,
+  { PublicKeyAlgorithm::ECDSA,
+    DigestAlgorithm::sha384,
+    { 0x06, 0x08,
       0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03 },
-    12,
+    10,
   },
-  { SignatureAlgorithm::ecdsa_with_sha256,
-    { 0x30, 0x0a, 0x06, 0x08,
+  { PublicKeyAlgorithm::ECDSA,
+    DigestAlgorithm::sha256,
+    { 0x06, 0x08,
       0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 },
-    12,
+    10,
   },
-  { SignatureAlgorithm::ecdsa_with_sha1,
-    { 0x30, 0x09, 0x06, 0x07,
+  { PublicKeyAlgorithm::ECDSA,
+    DigestAlgorithm::sha1,
+    { 0x06, 0x07,
       0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01 },
-    11,
+    9,
   },
 
   // RSA
-  { SignatureAlgorithm::rsa_pkcs1_with_sha512,
-    { 0x30, 0x0b, 0x06, 0x09,
+  { PublicKeyAlgorithm::RSA_PKCS1,
+    DigestAlgorithm::sha512,
+    { 0x06, 0x09,
       0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d },
-    13,
-  },
-  { SignatureAlgorithm::rsa_pkcs1_with_sha384,
-    { 0x30, 0x0b, 0x06, 0x09,
-      0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c },
-    13,
-  },
-  { SignatureAlgorithm::rsa_pkcs1_with_sha256,
-    { 0x30, 0x0b, 0x06, 0x09,
-      0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b },
-    13,
+    11,
   },
-  { SignatureAlgorithm::rsa_pkcs1_with_sha1,
-    // IETF Standard OID
-    { 0x30, 0x0b, 0x06, 0x09,
-      0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 },
-    13,
-  },
-  { SignatureAlgorithm::rsa_pkcs1_with_sha1,
-    // Legacy OIW OID (bug 1042479)
-    { 0x30, 0x07, 0x06, 0x05,
-      0x2b, 0x0e, 0x03, 0x02, 0x1d },
-    9,
-  },
-
-  // id-dsa-with-sha256 (2.16.840.1.101.3.4.3.2)
-  { SignatureAlgorithm::unsupported_algorithm,
-    { 0x30, 0x0b, 0x06, 0x09,
-      0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x02 },
-    13,
-  },
-
-  // id-dsa-with-sha1 (1.2.840.10040.4.3)
-  { SignatureAlgorithm::unsupported_algorithm,
-    { 0x30, 0x09, 0x06, 0x07,
-      0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x03 },
+  { PublicKeyAlgorithm::RSA_PKCS1,
+    DigestAlgorithm::sha384,
+    { 0x06, 0x09,
+      0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c },
     11,
   },
-
-  // RSA-with-MD5 (1.2.840.113549.1.1.4)
-  { SignatureAlgorithm::unsupported_algorithm,
-    { 0x30, 0x0b, 0x06, 0x09,
-      0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04 },
-    13,
+  { PublicKeyAlgorithm::RSA_PKCS1,
+    DigestAlgorithm::sha256,
+    { 0x06, 0x09,
+      0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b },
+    11,
   },
-
-  // id-sha256 (2.16.840.1.101.3.4.2.1). It is invalid because SHA-256 is not
-  // a signature algorithm.
-  { SignatureAlgorithm::unsupported_algorithm,
-    { 0x30, 0x0b, 0x06, 0x09,
-      0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 },
-    13,
+  { PublicKeyAlgorithm::RSA_PKCS1,
+    DigestAlgorithm::sha1,
+    // IETF Standard OID
+    { 0x06, 0x09,
+      0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 },
+    11,
+  },
+  { PublicKeyAlgorithm::RSA_PKCS1,
+    DigestAlgorithm::sha1,
+    // Legacy OIW OID (bug 1042479)
+    { 0x06, 0x05,
+      0x2b, 0x0e, 0x03, 0x02, 0x1d },
+    7,
   },
 };
 
-class pkixder_SignatureAlgorithmIdentifier
+class pkixder_SignatureAlgorithmIdentifierValue_Valid
   : public ::testing::Test
   , public ::testing::WithParamInterface<
-                AlgorithmIdentifierTestInfo<SignatureAlgorithm>>
+             ValidSignatureAlgorithmIdentifierValueTestInfo>
 {
 };
 
-TEST_P(pkixder_SignatureAlgorithmIdentifier, ValidAndInvalid)
+TEST_P(pkixder_SignatureAlgorithmIdentifierValue_Valid, Valid)
 {
-  const AlgorithmIdentifierTestInfo<SignatureAlgorithm>& param(GetParam());
+  const ValidSignatureAlgorithmIdentifierValueTestInfo& param(GetParam());
 
   {
     Input input;
     ASSERT_EQ(Success, input.Init(param.der, param.derLength));
     Reader reader(input);
-    SignatureAlgorithm alg;
-    ASSERT_EQ(Success, SignatureAlgorithmIdentifier(reader, alg));
-    ASSERT_EQ(param.algorithm, alg);
+    PublicKeyAlgorithm publicKeyAlg;
+    DigestAlgorithm digestAlg;
+    ASSERT_EQ(Success,
+              SignatureAlgorithmIdentifierValue(reader, publicKeyAlg,
+                                                digestAlg));
+    ASSERT_EQ(param.publicKeyAlg, publicKeyAlg);
+    ASSERT_EQ(param.digestAlg, digestAlg);
     ASSERT_EQ(Success, End(reader));
   }
 
   {
     uint8_t derWithNullParam[MAX_ALGORITHM_OID_DER_LENGTH + 2];
     memcpy(derWithNullParam, param.der, param.derLength);
-    derWithNullParam[1] += 2; // we're going to expand the value by 2 bytes
     derWithNullParam[param.derLength] = 0x05; // NULL tag
     derWithNullParam[param.derLength + 1] = 0x00; // length zero
 
     Input input;
     ASSERT_EQ(Success, input.Init(derWithNullParam, param.derLength + 2));
     Reader reader(input);
-    SignatureAlgorithm alg;
-    ASSERT_EQ(Success, SignatureAlgorithmIdentifier(reader, alg));
-    ASSERT_EQ(param.algorithm, alg);
+    PublicKeyAlgorithm publicKeyAlg;
+    DigestAlgorithm digestAlg;
+    ASSERT_EQ(Success,
+              SignatureAlgorithmIdentifierValue(reader, publicKeyAlg,
+                                                digestAlg));
+    ASSERT_EQ(param.publicKeyAlg, publicKeyAlg);
+    ASSERT_EQ(param.digestAlg, digestAlg);
     ASSERT_EQ(Success, End(reader));
   }
 }
 
-INSTANTIATE_TEST_CASE_P(pkixder_SignatureAlgorithmIdentifier,
-                        pkixder_SignatureAlgorithmIdentifier,
-                        testing::ValuesIn(SIGNATURE_ALGORITHM_TEST_INFO));
+INSTANTIATE_TEST_CASE_P(
+  pkixder_SignatureAlgorithmIdentifierValue_Valid,
+  pkixder_SignatureAlgorithmIdentifierValue_Valid,
+  testing::ValuesIn(VALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO));
+
+static const InvalidAlgorithmIdentifierTestInfo
+  INVALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO[] =
+{
+  // id-dsa-with-sha256 (2.16.840.1.101.3.4.3.2)
+  { { 0x06, 0x09,
+      0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x02 },
+    11,
+  },
+
+  // id-dsa-with-sha1 (1.2.840.10040.4.3)
+  { { 0x06, 0x07,
+      0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x03 },
+    9,
+  },
+
+  // RSA-with-MD5 (1.2.840.113549.1.1.4)
+  { { 0x06, 0x09,
+      0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04 },
+    11,
+  },
+
+  // id-sha256 (2.16.840.1.101.3.4.2.1). It is invalid because SHA-256 is not
+  // a signature algorithm.
+  { { 0x06, 0x09,
+      0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 },
+    11,
+  },
+};
+
+class pkixder_SignatureAlgorithmIdentifier_Invalid
+  : public ::testing::Test
+  , public ::testing::WithParamInterface<InvalidAlgorithmIdentifierTestInfo>
+{
+};
+
+TEST_P(pkixder_SignatureAlgorithmIdentifier_Invalid, Invalid)
+{
+  const InvalidAlgorithmIdentifierTestInfo& param(GetParam());
+  Input input;
+  ASSERT_EQ(Success, input.Init(param.der, param.derLength));
+  Reader reader(input);
+  der::PublicKeyAlgorithm publicKeyAlg;
+  DigestAlgorithm digestAlg;
+  ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
+            SignatureAlgorithmIdentifierValue(reader, publicKeyAlg, digestAlg));
+}
+
+INSTANTIATE_TEST_CASE_P(
+  pkixder_SignatureAlgorithmIdentifier_Invalid,
+  pkixder_SignatureAlgorithmIdentifier_Invalid,
+  testing::ValuesIn(INVALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO));
 
 } // unnamed namespace
--- a/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
+++ b/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
@@ -54,37 +54,46 @@ private:
   }
 
   Result IsChainValid(const DERArray&, Time) override
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  Result VerifySignedData(const SignedDataWithSignature&, Input) override
+  Result DigestBuf(Input item, DigestAlgorithm digestAlg,
+                   /*out*/ uint8_t *digestBuf, size_t digestBufLen)
+                   override
+  {
+    return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen);
+  }
+
+  Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
+                                            final override
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  Result DigestBuf(Input item, /*out*/ uint8_t *digestBuf, size_t digestBufLen)
-                   override
+  Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override
   {
-    return TestDigestBuf(item, digestBuf, digestBufLen);
+    ADD_FAILURE();
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
-                                            override
+  Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) final override
   {
-    return Success;
+    ADD_FAILURE();
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
+  Result VerifyECDSASignedDigest(const SignedDigest&, Input) override
   {
-    return Success;
+    ADD_FAILURE();
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 };
 
 class pkixocsp_CreateEncodedOCSPRequest : public ::testing::Test
 {
 protected:
   void MakeIssuerCertIDComponents(const char* issuerASCII,
                                   /*out*/ ByteString& issuerDER,
--- a/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
+++ b/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
@@ -65,39 +65,46 @@ public:
   }
 
   Result IsChainValid(const DERArray&, Time) final override
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  Result VerifySignedData(const SignedDataWithSignature& signedData,
-                          Input subjectPublicKeyInfo) final override
-  {
-    return TestVerifySignedData(signedData, subjectPublicKeyInfo);
-  }
-
-  Result DigestBuf(Input item, /*out*/ uint8_t* digestBuf, size_t digestBufLen)
+  Result DigestBuf(Input item, DigestAlgorithm digestAlg,
+                   /*out*/ uint8_t* digestBuf, size_t digestBufLen)
                    final override
   {
-    return TestDigestBuf(item, digestBuf, digestBufLen);
+    return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen);
   }
 
   Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
                                             final override
   {
     return Success;
   }
 
+  Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+                                    Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
   Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) final override
   {
     return Success;
   }
 
+  Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                                 Input subjectPublicKeyInfo) override
+  {
+    return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
+  }
+
   OCSPTestTrustDomain(const OCSPTestTrustDomain&) = delete;
   void operator=(const OCSPTestTrustDomain&) = delete;
 };
 
 namespace {
 char const* const rootName = "Test CA 1";
 void deleteCertID(CertID* certID) { delete certID; }
 } // unnamed namespace
--- a/security/pkix/test/lib/pkixtestnss.cpp
+++ b/security/pkix/test/lib/pkixtestnss.cpp
@@ -380,23 +380,36 @@ SHA1(const ByteString& toHash)
                                static_cast<int32_t>(toHash.length()));
   if (srv != SECSuccess) {
     return ByteString();
   }
   return ByteString(digestBuf, sizeof(digestBuf));
 }
 
 Result
-TestVerifySignedData(const SignedDataWithSignature& signedData,
-                     Input subjectPublicKeyInfo)
+TestVerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                            Input subjectPublicKeyInfo)
 {
   InitNSSIfNeeded();
-  return VerifySignedDataNSS(signedData, subjectPublicKeyInfo, nullptr);
+  return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+                                    nullptr);
 }
 
 Result
-TestDigestBuf(Input item, /*out*/ uint8_t* digestBuf, size_t digestBufLen)
+TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+                               Input subjectPublicKeyInfo)
 {
   InitNSSIfNeeded();
-  return DigestBufNSS(item, digestBuf, digestBufLen);
+  return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
+                                       nullptr);
+}
+
+Result
+TestDigestBuf(Input item,
+              DigestAlgorithm digestAlg,
+              /*out*/ uint8_t* digestBuf,
+              size_t digestBufLen)
+{
+  InitNSSIfNeeded();
+  return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
 }
 
 } } } // namespace mozilla::pkix::test
--- a/security/pkix/test/lib/pkixtestutil.h
+++ b/security/pkix/test/lib/pkixtestutil.h
@@ -267,21 +267,22 @@ protected:
 TestKeyPair* CloneReusedKeyPair();
 TestKeyPair* GenerateKeyPair();
 TestKeyPair* GenerateDSSKeyPair();
 inline void DeleteTestKeyPair(TestKeyPair* keyPair) { delete keyPair; }
 typedef ScopedPtr<TestKeyPair, DeleteTestKeyPair> ScopedTestKeyPair;
 
 ByteString SHA1(const ByteString& toHash);
 
-Result TestCheckPublicKey(Input subjectPublicKeyInfo);
-Result TestVerifySignedData(const SignedDataWithSignature& signedData,
-                            Input subjectPublicKeyInfo);
-Result TestDigestBuf(Input item, /*out*/ uint8_t* digestBuf,
-                     size_t digestBufLen);
+Result TestVerifyECDSASignedDigest(const SignedDigest& signedDigest,
+                                   Input subjectPublicKeyInfo);
+Result TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
+                                      Input subjectPublicKeyInfo);
+Result TestDigestBuf(Input item, DigestAlgorithm digestAlg,
+                     /*out*/ uint8_t* digestBuf, size_t digestBufLen);
 
 // Replace one substring in item with another of the same length, but only if
 // the substring was found exactly once. The "same length" restriction is
 // useful for avoiding invalidating lengths encoded within the item. The
 // "only once" restriction is helpful for avoiding making accidental changes.
 //
 // The string to search for must be 8 or more bytes long so that it is
 // extremely unlikely that there will ever be any false positive matches