Bug 1043041: Use mozilla::pkix::Time instead of PRTime, r=keeler
authorBrian Smith <brian@briansmith.org>
Sat, 02 Aug 2014 08:49:12 -0700
changeset 197619 a4a8b3b58191206f53748d823cf255fba4042253
parent 197618 64719bb171797b81c6d155251da939904777fa31
child 197620 2919311231ecd1bd53e18eee26bc7106b2ab806c
push id27249
push userryanvm@gmail.com
push dateMon, 04 Aug 2014 20:14:35 +0000
treeherdermozilla-central@7f81be7db528 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1043041
milestone34.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 1043041: Use mozilla::pkix::Time instead of PRTime, r=keeler
security/apps/AppSignatureVerification.cpp
security/apps/AppTrustDomain.cpp
security/apps/AppTrustDomain.h
security/certverifier/CertVerifier.cpp
security/certverifier/CertVerifier.h
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/NSSCertDBTrustDomain.h
security/certverifier/OCSPCache.cpp
security/certverifier/OCSPCache.h
security/manager/boot/src/PublicKeyPinningService.cpp
security/manager/boot/src/PublicKeyPinningService.h
security/manager/ssl/src/SSLServerCertVerification.cpp
security/manager/ssl/src/nsDataSignatureVerifier.cpp
security/manager/ssl/src/nsNSSCertificate.cpp
security/manager/ssl/src/nsNSSCertificateDB.cpp
security/manager/ssl/src/nsUsageArrayHelper.cpp
security/manager/ssl/src/nsUsageArrayHelper.h
security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
security/pkix/include/pkix/Time.h
security/pkix/include/pkix/pkix.h
security/pkix/include/pkix/pkixtypes.h
security/pkix/lib/pkixbuild.cpp
security/pkix/lib/pkixcheck.cpp
security/pkix/lib/pkixcheck.h
security/pkix/lib/pkixder.cpp
security/pkix/lib/pkixder.h
security/pkix/lib/pkixocsp.cpp
security/pkix/lib/pkixtime.cpp
security/pkix/lib/pkixutil.h
security/pkix/moz.build
security/pkix/test/gtest/nssgtest.cpp
security/pkix/test/gtest/nssgtest.h
security/pkix/test/gtest/pkixbuild_tests.cpp
security/pkix/test/gtest/pkixcert_extension_tests.cpp
security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
security/pkix/test/gtest/pkixder_universal_types_tests.cpp
security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
security/pkix/test/lib/pkixtestutil.cpp
security/pkix/test/lib/pkixtestutil.h
--- a/security/apps/AppSignatureVerification.cpp
+++ b/security/apps/AppSignatureVerification.cpp
@@ -542,17 +542,18 @@ VerifyCertificate(CERTCertificate* signe
   if (trustDomain.SetTrustedRoot(context.trustedRoot) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
   Input certDER;
   Result rv = certDER.Init(signerCert->derCert.data, signerCert->derCert.len);
   if (rv != Success) {
     return mozilla::psm::GetXPCOMFromNSSError(MapResultToPRErrorCode(rv));
   }
-  rv = BuildCertChain(trustDomain, certDER, PR_Now(),
+
+  rv = BuildCertChain(trustDomain, certDER, Now(),
                       EndEntityOrCA::MustBeEndEntity,
                       KeyUsage::digitalSignature,
                       KeyPurposeId::id_kp_codeSigning,
                       CertPolicyId::anyPolicy,
                       nullptr/*stapledOCSPResponse*/);
   if (rv != Success) {
     return mozilla::psm::GetXPCOMFromNSSError(MapResultToPRErrorCode(rv));
   }
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -90,17 +90,17 @@ AppTrustDomain::SetTrustedRoot(AppTruste
     return SECFailure;
   }
 
   return SECSuccess;
 }
 
 Result
 AppTrustDomain::FindIssuer(Input encodedIssuerName, IssuerChecker& checker,
-                           PRTime time)
+                           Time)
 
 {
   MOZ_ASSERT(mTrustedRoot);
   if (!mTrustedRoot) {
     return Result::FATAL_ERROR_INVALID_STATE;
   }
 
   // TODO(bug 1035418): If/when mozilla::pkix relaxes the restriction that
@@ -210,17 +210,17 @@ AppTrustDomain::VerifySignedData(const S
 Result
 AppTrustDomain::DigestBuf(Input item, /*out*/ uint8_t* digestBuf,
                           size_t digestBufLen)
 {
   return ::mozilla::pkix::DigestBuf(item, digestBuf, digestBufLen);
 }
 
 Result
-AppTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, PRTime time,
+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
   // certificate, we will use the active distrust mechanism.
   return Success;
 }
 
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -25,19 +25,20 @@ public:
 
   virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                               const mozilla::pkix::CertPolicyId& policy,
                               mozilla::pkix::Input candidateCertDER,
                               /*out*/ mozilla::pkix::TrustLevel& trustLevel)
                               MOZ_OVERRIDE;
   virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
                             IssuerChecker& checker,
-                            PRTime time) MOZ_OVERRIDE;
+                            mozilla::pkix::Time time) MOZ_OVERRIDE;
   virtual Result CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
-                                 const mozilla::pkix::CertID& certID, PRTime time,
+                                 const mozilla::pkix::CertID& certID,
+                                 mozilla::pkix::Time time,
                     /*optional*/ const mozilla::pkix::Input* stapledOCSPresponse,
                     /*optional*/ const mozilla::pkix::Input* aiaExtension);
   virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain)
                               MOZ_OVERRIDE;
   virtual Result CheckPublicKey(mozilla::pkix::Input subjectPublicKeyInfo)
                                 MOZ_OVERRIDE;
   virtual Result VerifySignedData(
            const mozilla::pkix::SignedDataWithSignature& signedData,
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -81,17 +81,17 @@ IsCertBuiltInRoot(CERTCertificate* cert,
   return SECSuccess;
 }
 
 struct ChainValidationCallbackState
 {
   const char* hostname;
   const CertVerifier::pinning_enforcement_config pinningEnforcementLevel;
   const SECCertificateUsage usage;
-  const PRTime time;
+  const Time time;
 };
 
 SECStatus chainValidationCallback(void* state, const CERTCertList* certList,
                                   PRBool* chainOK)
 {
   ChainValidationCallbackState* callbackState =
     reinterpret_cast<ChainValidationCallbackState*>(state);
 
@@ -152,17 +152,17 @@ SECStatus chainValidationCallback(void* 
     ChainHasValidPins(certList, callbackState->hostname, callbackState->time,
                       enforceTestMode);
 
   return SECSuccess;
 }
 
 static Result
 BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, Input certDER,
-                             PRTime time, KeyUsage ku1, KeyUsage ku2,
+                             Time time, KeyUsage ku1, KeyUsage ku2,
                              KeyUsage ku3, KeyPurposeId eku,
                              const CertPolicyId& requiredPolicy,
                              const Input* stapledOCSPResponse)
 {
   Result rv = BuildCertChain(trustDomain, certDER, time,
                              EndEntityOrCA::MustBeEndEntity, ku1,
                              eku, requiredPolicy, stapledOCSPResponse);
   if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
@@ -178,17 +178,17 @@ BuildCertChainForOneKeyUsage(TrustDomain
       }
     }
   }
   return rv;
 }
 
 SECStatus
 CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
-                         PRTime time, void* pinArg, const char* hostname,
+                         Time time, void* pinArg, const char* hostname,
                          const Flags flags,
             /*optional*/ const SECItem* stapledOCSPResponseSECItem,
         /*optional out*/ ScopedCERTCertList* builtChain,
         /*optional out*/ SECOidTag* evOidPolicy)
 {
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of VerifyCert\n"));
 
   PR_ASSERT(cert);
@@ -424,17 +424,17 @@ CertVerifier::VerifyCert(CERTCertificate
   }
 
   return SECSuccess;
 }
 
 SECStatus
 CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
                      /*optional*/ const SECItem* stapledOCSPResponse,
-                                  PRTime time,
+                                  Time time,
                      /*optional*/ void* pinarg,
                                   const char* hostname,
                                   bool saveIntermediatesInPermanentDatabase,
                  /*optional out*/ ScopedCERTCertList* builtChain,
                  /*optional out*/ SECOidTag* evOidPolicy)
 {
   PR_ASSERT(peerCert);
   // XXX: PR_ASSERT(pinarg)
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -23,28 +23,28 @@ public:
   static const Flags FLAG_LOCAL_ONLY;
   // Don't perform fallback DV validation on EV validation failure.
   static const Flags FLAG_MUST_BE_EV;
 
   // *evOidPolicy == SEC_OID_UNKNOWN means the cert is NOT EV
   // Only one usage per verification is supported.
   SECStatus VerifyCert(CERTCertificate* cert,
                        SECCertificateUsage usage,
-                       PRTime time,
+                       mozilla::pkix::Time time,
                        void* pinArg,
                        const char* hostname,
                        Flags flags = 0,
        /*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
       /*optional out*/ ScopedCERTCertList* builtChain = nullptr,
       /*optional out*/ SECOidTag* evOidPolicy = nullptr);
 
   SECStatus VerifySSLServerCert(
                     CERTCertificate* peerCert,
        /*optional*/ const SECItem* stapledOCSPResponse,
-                    PRTime time,
+                    mozilla::pkix::Time time,
        /*optional*/ void* pinarg,
                     const char* hostname,
                     bool saveIntermediatesInPermanentDatabase = false,
    /*optional out*/ ScopedCERTCertList* builtChain = nullptr,
    /*optional out*/ SECOidTag* evOidPolicy = nullptr);
 
   enum pinning_enforcement_config {
     pinningDisabled = 0,
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -28,16 +28,18 @@
 
 using namespace mozilla;
 using namespace mozilla::pkix;
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gCertVerifierLog;
 #endif
 
+static const uint64_t ServerFailureDelaySeconds = 5 * 60;
+
 namespace mozilla { namespace psm {
 
 const char BUILTIN_ROOTS_MODULE_DEFAULT_NAME[] = "Builtin Roots Module";
 
 void PORT_Free_string(char* str) { PORT_Free(str); }
 
 namespace {
 
@@ -92,17 +94,17 @@ static const uint8_t PERMIT_FRANCE_GOV_N
                        "\x30\x05\x82\x03" ".mf"
                        "\x30\x05\x82\x03" ".wf"
                        "\x30\x05\x82\x03" ".pf"
                        "\x30\x05\x82\x03" ".nc"
                        "\x30\x05\x82\x03" ".tf";
 
 Result
 NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
-                                 IssuerChecker& checker, PRTime time)
+                                 IssuerChecker& checker, Time)
 {
   // TODO: NSS seems to be ambiguous between "no potential issuers found" and
   // "there was an error trying to retrieve the potential issuers."
   SECItem encodedIssuerNameSECItem = UnsafeMapInputToSECItem(encodedIssuerName);
   ScopedCERTCertList
     candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
                                           &encodedIssuerNameSECItem, 0,
                                           false));
@@ -302,17 +304,17 @@ GetOCSPAuthorityInfoAccessLocation(PLAre
     }
   }
 
   return Success;
 }
 
 Result
 NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
-                                      const CertID& certID, PRTime time,
+                                      const CertID& certID, Time time,
                          /*optional*/ const Input* stapledOCSPResponse,
                          /*optional*/ const Input* aiaExtension)
 {
   // Actively distrusted certificates will have already been blocked by
   // GetCertTrust.
 
   // TODO: need to verify that IsRevoked isn't called for trust anchors AND
   // that that fact is documented in mozillapkix.
@@ -367,17 +369,17 @@ NSSCertDBTrustDomain::CheckRevocation(En
   } else {
     // no stapled OCSP response
     Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 2);
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: no stapled OCSP response"));
   }
 
   Result cachedResponseResult = Success;
-  PRTime cachedResponseValidThrough = 0;
+  Time cachedResponseValidThrough(Time::uninitialized);
   bool cachedResponsePresent = mOCSPCache.Get(certID,
                                               cachedResponseResult,
                                               cachedResponseValidThrough);
   if (cachedResponsePresent) {
     if (cachedResponseResult == Success && cachedResponseValidThrough >= time) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: cached OCSP response: good"));
       return Success;
@@ -517,17 +519,20 @@ NSSCertDBTrustDomain::CheckRevocation(En
   } else {
     rv = cachedResponseResult;
     attemptedRequest = false;
   }
 
   if (response.GetLength() == 0) {
     Result error = rv;
     if (attemptedRequest) {
-      PRTime timeout = time + ServerFailureDelay;
+      Time timeout(time);
+      if ( timeout.AddSeconds(ServerFailureDelaySeconds) != Success) {
+        return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
+      }
       rv = mOCSPCache.Put(certID, error, time, timeout);
       if (rv != Success) {
         return rv;
       }
     }
     if (mOCSPFetching != FetchOCSPForDVSoftFail) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: returning SECFailure after "
@@ -581,38 +586,41 @@ NSSCertDBTrustDomain::CheckRevocation(En
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
          ("NSSCertDBTrustDomain: end of CheckRevocation"));
 
   return Success; // Soft fail -> success :(
 }
 
 Result
 NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
-  const CertID& certID, PRTime time, uint16_t maxLifetimeInDays,
+  const CertID& certID, Time time, uint16_t maxLifetimeInDays,
   Input encodedResponse, EncodedResponseSource responseSource,
   /*out*/ bool& expired)
 {
-  PRTime thisUpdate = 0;
-  PRTime validThrough = 0;
+  Time thisUpdate(Time::uninitialized);
+  Time validThrough(Time::uninitialized);
   Result rv = VerifyEncodedOCSPResponse(*this, 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;
   }
   // validThrough is only trustworthy if the response successfully verifies
   // or it indicates a revoked or unknown certificate.
   // If this isn't the case, store an indication of failure (to prevent
   // repeatedly requesting a response from a failing server).
   if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
       rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
-    validThrough = time + ServerFailureDelay;
+    validThrough = time;
+    if (validThrough.AddSeconds(ServerFailureDelaySeconds) != Success) {
+      return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
+    }
   }
   if (responseSource == ResponseIsFromNetwork ||
       rv == Success ||
       rv == Result::ERROR_REVOKED_CERTIFICATE ||
       rv == Result::ERROR_OCSP_UNKNOWN_CERT) {
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: caching OCSP response"));
     Result putRV = mOCSPCache.Put(certID, rv, thisUpdate, validThrough);
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -53,17 +53,17 @@ public:
   NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
                        OCSPCache& ocspCache, void* pinArg,
                        CertVerifier::ocsp_get_config ocspGETConfig,
           /*optional*/ CERTChainVerifyCallback* checkChainCallback = nullptr,
           /*optional*/ ScopedCERTCertList* builtChain = nullptr);
 
   virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
                             IssuerChecker& checker,
-                            PRTime time) MOZ_OVERRIDE;
+                            mozilla::pkix::Time time) MOZ_OVERRIDE;
 
   virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                               const mozilla::pkix::CertPolicyId& policy,
                               mozilla::pkix::Input candidateCertDER,
                               /*out*/ mozilla::pkix::TrustLevel& trustLevel)
                               MOZ_OVERRIDE;
 
   virtual Result CheckPublicKey(mozilla::pkix::Input subjectPublicKeyInfo)
@@ -76,32 +76,31 @@ public:
 
   virtual Result DigestBuf(mozilla::pkix::Input item,
                            /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) MOZ_OVERRIDE;
 
   virtual Result CheckRevocation(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    const mozilla::pkix::CertID& certID,
-                   PRTime time,
+                   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)
                               MOZ_OVERRIDE;
 
 private:
   enum EncodedResponseSource {
     ResponseIsFromNetwork = 1,
     ResponseWasStapled = 2
   };
-  static const PRTime ServerFailureDelay = 5 * 60 * PR_USEC_PER_SEC;
   Result VerifyAndMaybeCacheEncodedOCSPResponse(
-    const mozilla::pkix::CertID& certID, PRTime time,
+    const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
     uint16_t maxLifetimeInDays, mozilla::pkix::Input encodedResponse,
     EncodedResponseSource responseSource, /*out*/ bool& expired);
 
   const SECTrustType mCertDBTrustType;
   const OCSPFetching mOCSPFetching;
   OCSPCache& mOCSPCache; // non-owning!
   void* mPinArg; // non-owning!
   const CertVerifier::ocsp_get_config mOCSPGetConfig;
--- a/security/certverifier/OCSPCache.cpp
+++ b/security/certverifier/OCSPCache.cpp
@@ -85,22 +85,18 @@ CertIDHash(SHA384Buffer& buf, const Cert
   rv = PK11_DigestFinal(context.get(), buf, &outLen, SHA384_LENGTH);
   if (outLen != SHA384_LENGTH) {
     return SECFailure;
   }
   return rv;
 }
 
 Result
-OCSPCache::Entry::Init(const CertID& aCertID, Result aResult,
-                       PRTime aThisUpdate, PRTime aValidThrough)
+OCSPCache::Entry::Init(const CertID& aCertID)
 {
-  mResult = aResult;
-  mThisUpdate = aThisUpdate;
-  mValidThrough = aValidThrough;
   SECStatus srv = CertIDHash(mIDHash, aCertID);
   if (srv != SECSuccess) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
   return Success;
 }
 
 OCSPCache::OCSPCache()
@@ -154,17 +150,17 @@ OCSPCache::MakeMostRecentlyUsed(size_t a
   Entry* entry = mEntries[aIndex];
   // Since mEntries is sorted with the most-recently-used entry at the end,
   // aIndex is likely to be near the end, so this is likely to be fast.
   mEntries.erase(mEntries.begin() + aIndex);
   mEntries.append(entry);
 }
 
 bool
-OCSPCache::Get(const CertID& aCertID, Result& aResult, PRTime& aValidThrough)
+OCSPCache::Get(const CertID& aCertID, Result& aResult, Time& aValidThrough)
 {
   MutexAutoLock lock(mMutex);
 
   size_t index;
   if (!FindInternal(aCertID, index, lock)) {
     LogWithCertID("OCSPCache::Get(%p) not in cache", aCertID);
     return false;
   }
@@ -172,17 +168,17 @@ OCSPCache::Get(const CertID& aCertID, Re
   aResult = mEntries[index]->mResult;
   aValidThrough = mEntries[index]->mValidThrough;
   MakeMostRecentlyUsed(index, lock);
   return true;
 }
 
 Result
 OCSPCache::Put(const CertID& aCertID, Result aResult,
-               PRTime aThisUpdate, PRTime aValidThrough)
+               Time aThisUpdate, Time aValidThrough)
 {
   MutexAutoLock lock(mMutex);
 
   size_t index;
   if (FindInternal(aCertID, index, lock)) {
     // Never replace an entry indicating a revoked certificate.
     if (mEntries[index]->mResult == Result::ERROR_REVOKED_CERTIFICATE) {
       LogWithCertID("OCSPCache::Put(%p) already in cache as revoked - "
@@ -240,24 +236,25 @@ OCSPCache::Put(const CertID& aCertID, Re
     // security issue. If we're trying to insert a revoked or unknown response,
     // we can't. We should return with an error that causes the current
     // verification to fail.
     if (mEntries.length() == MaxEntries) {
       return aResult;
     }
   }
 
-  Entry* newEntry = new (std::nothrow) Entry();
+  Entry* newEntry = new (std::nothrow) Entry(aResult, aThisUpdate,
+                                             aValidThrough);
   // Normally we don't have to do this in Gecko, because OOM is fatal.
   // However, if we want to embed this in another project, OOM might not
   // be fatal, so handle this case.
   if (!newEntry) {
     return Result::FATAL_ERROR_NO_MEMORY;
   }
-  Result rv = newEntry->Init(aCertID, aResult, aThisUpdate, aValidThrough);
+  Result rv = newEntry->Init(aCertID);
   if (rv != Success) {
     delete newEntry;
     return rv;
   }
   mEntries.append(newEntry);
   LogWithCertID("OCSPCache::Put(%p) added to cache", aCertID);
   return Success;
 }
--- a/security/certverifier/OCSPCache.h
+++ b/security/certverifier/OCSPCache.h
@@ -24,18 +24,18 @@
 
 #ifndef mozilla_psm_OCSPCache_h
 #define mozilla_psm_OCSPCache_h
 
 #include "hasht.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Vector.h"
 #include "pkix/Result.h"
+#include "pkix/Time.h"
 #include "prerror.h"
-#include "prtime.h"
 #include "seccomon.h"
 
 namespace mozilla { namespace pkix {
 struct CertID;
 } } // namespace mozilla::pkix
 
 namespace mozilla { namespace psm {
 
@@ -55,45 +55,52 @@ public:
   ~OCSPCache();
 
   // Returns true if the status of the given certificate (issued by the given
   // issuer) is in the cache, and false otherwise.
   // If it is in the cache, returns by reference the error code of the cached
   // status and the time through which the status is considered trustworthy.
   bool Get(const mozilla::pkix::CertID& aCertID,
            /*out*/ mozilla::pkix::Result& aResult,
-           /*out*/ PRTime& aValidThrough);
+           /*out*/ mozilla::pkix::Time& aValidThrough);
 
   // Caches the status of the given certificate (issued by the given issuer).
   // The status is considered trustworthy through the given time.
   // A status with an error code of SEC_ERROR_REVOKED_CERTIFICATE will not
   // be replaced or evicted.
   // A status with an error code of SEC_ERROR_OCSP_UNKNOWN_CERT will not
   // be evicted when the cache is full.
   // A status with a more recent thisUpdate will not be replaced with a
   // status with a less recent thisUpdate unless the less recent status
   // indicates the certificate is revoked.
   mozilla::pkix::Result Put(const mozilla::pkix::CertID& aCertID,
-                            mozilla::pkix::Result aResult, PRTime aThisUpdate,
-                            PRTime aValidThrough);
+                            mozilla::pkix::Result aResult,
+                            mozilla::pkix::Time aThisUpdate,
+                            mozilla::pkix::Time aValidThrough);
 
   // Removes everything from the cache.
   void Clear();
 
 private:
   class Entry
   {
   public:
-    mozilla::pkix::Result Init(const mozilla::pkix::CertID& aCertID,
-                               mozilla::pkix::Result aResult,
-                               PRTime aThisUpdate, PRTime aValidThrough);
+    Entry(mozilla::pkix::Result aResult,
+          mozilla::pkix::Time aThisUpdate,
+          mozilla::pkix::Time aValidThrough)
+      : mResult(aResult)
+      , mThisUpdate(aThisUpdate)
+      , mValidThrough(aValidThrough)
+    {
+    }
+    mozilla::pkix::Result Init(const mozilla::pkix::CertID& aCertID);
 
     mozilla::pkix::Result mResult;
-    PRTime mThisUpdate;
-    PRTime mValidThrough;
+    mozilla::pkix::Time mThisUpdate;
+    mozilla::pkix::Time mValidThrough;
     // The SHA-384 hash of the concatenation of the DER encodings of the
     // issuer name and issuer key, followed by the serial number.
     // See the documentation for CertIDHash in OCSPCache.cpp.
     SHA384Buffer mIDHash;
   };
 
   bool FindInternal(const mozilla::pkix::CertID& aCertID, /*out*/ size_t& index,
                     const MutexAutoLock& aProofOfLock);
--- a/security/manager/boot/src/PublicKeyPinningService.cpp
+++ b/security/manager/boot/src/PublicKeyPinningService.cpp
@@ -13,16 +13,17 @@
 #include "nssb64.h"
 #include "pkix/pkixtypes.h"
 #include "prlog.h"
 #include "ScopedNSSTypes.h"
 #include "seccomon.h"
 #include "sechash.h"
 
 using namespace mozilla;
+using namespace mozilla::pkix;
 using namespace mozilla::psm;
 
 #if defined(PR_LOGGING)
 PRLogModuleInfo* gPublicKeyPinningLog =
   PR_NewLogModule("PublicKeyPinningService");
 #endif
 
 /**
@@ -284,22 +285,22 @@ CheckChainAgainstAllNames(const CERTCert
   } while (currentName != nameList);
 
   return hasValidPins;
 }
 
 bool
 PublicKeyPinningService::ChainHasValidPins(const CERTCertList* certList,
                                            const char* hostname,
-                                           const PRTime time,
+                                           mozilla::pkix::Time time,
                                            bool enforceTestMode)
 {
   if (!certList) {
     return false;
   }
-  if (time > kPreloadPKPinsExpirationTime) {
+  if (time > TimeFromElapsedSecondsAD(kPreloadPKPinsExpirationTime)) {
     return true;
   }
   if (!hostname || hostname[0] == 0) {
     return CheckChainAgainstAllNames(certList, enforceTestMode);
   }
   return CheckPinsForHostname(certList, hostname, enforceTestMode);
 }
--- a/security/manager/boot/src/PublicKeyPinningService.h
+++ b/security/manager/boot/src/PublicKeyPinningService.h
@@ -1,16 +1,17 @@
 /* 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 PublicKeyPinningService_h
 #define PublicKeyPinningService_h
 
 #include "cert.h"
+#include "pkix/Time.h"
 
 namespace mozilla {
 namespace psm {
 
 class PublicKeyPinningService
 {
 public:
   /**
@@ -21,15 +22,15 @@ public:
    * possible names for the EE cert (Common Name (CN) plus all DNS Name:
    * subject Alt Name entries). The certList's head is the EE cert and the
    * tail is the trust anchor.
    * Note: if an alt name is a wildcard, it won't necessarily find a pinset
    * that would otherwise be valid for it
    */
   static bool ChainHasValidPins(const CERTCertList* certList,
                                 const char* hostname,
-                                const PRTime time,
+                                mozilla::pkix::Time time,
                                 bool enforceTestMode);
 };
 
 }} // namespace mozilla::psm
 
 #endif // PublicKeyPinningServiceService_h
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -125,16 +125,18 @@
 #include "secerr.h"
 #include "secport.h"
 #include "sslerr.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gPIPNSSLog;
 #endif
 
+using namespace mozilla::pkix;
+
 namespace mozilla { namespace psm {
 
 namespace {
 
 // do not use a nsCOMPtr to avoid static initializer/destructor
 nsIThreadPool* gCertVerificationThreadPool = nullptr;
 
 // We avoid using a mutex for the success case to avoid lock-related
@@ -616,48 +618,53 @@ class SSLServerCertVerificationJob : pub
 public:
   // Must be called only on the socket transport thread
   static SECStatus Dispatch(const RefPtr<SharedCertVerifier>& certVerifier,
                             const void* fdForLogging,
                             TransportSecurityInfo* infoObject,
                             CERTCertificate* serverCert,
                             SECItem* stapledOCSPResponse,
                             uint32_t providerFlags,
-                            PRTime time);
+                            Time time,
+                            PRTime prtime);
 private:
   NS_DECL_NSIRUNNABLE
 
   // Must be called only on the socket transport thread
   SSLServerCertVerificationJob(const RefPtr<SharedCertVerifier>& certVerifier,
                                const void* fdForLogging,
                                TransportSecurityInfo* infoObject,
                                CERTCertificate* cert,
                                SECItem* stapledOCSPResponse,
                                uint32_t providerFlags,
-                               PRTime time);
+                               Time time,
+                               PRTime prtime);
   const RefPtr<SharedCertVerifier> mCertVerifier;
   const void* const mFdForLogging;
   const RefPtr<TransportSecurityInfo> mInfoObject;
   const ScopedCERTCertificate mCert;
   const uint32_t mProviderFlags;
-  const PRTime mTime;
+  const Time mTime;
+  const PRTime mPRTime;
   const TimeStamp mJobStartTime;
   const ScopedSECItem mStapledOCSPResponse;
 };
 
 SSLServerCertVerificationJob::SSLServerCertVerificationJob(
     const RefPtr<SharedCertVerifier>& certVerifier, const void* fdForLogging,
     TransportSecurityInfo* infoObject, CERTCertificate* cert,
-    SECItem* stapledOCSPResponse, uint32_t providerFlags, PRTime time)
+    SECItem* stapledOCSPResponse, uint32_t providerFlags,
+    Time time, PRTime prtime)
   : mCertVerifier(certVerifier)
   , mFdForLogging(fdForLogging)
   , mInfoObject(infoObject)
   , mCert(CERT_DupCertificate(cert))
   , mProviderFlags(providerFlags)
   , mTime(time)
+  , mPRTime(prtime)
   , mJobStartTime(TimeStamp::Now())
   , mStapledOCSPResponse(SECITEM_DupItem(stapledOCSPResponse))
 {
 }
 
 // This function assumes that we will only use the SPDY connection coalescing
 // feature on connections where we have negotiated SPDY using NPN. If we ever
 // talk SPDY without having negotiated it with SPDY, this code will give wrong
@@ -723,17 +730,17 @@ BlockServerCertChangeForSpdy(nsNSSSocket
          ("SPDY Refused to allow new cert during renegotiation\n"));
   PR_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, 0);
   return SECFailure;
 }
 
 SECStatus
 AuthCertificate(CertVerifier& certVerifier, TransportSecurityInfo* infoObject,
                 CERTCertificate* cert, SECItem* stapledOCSPResponse,
-                uint32_t providerFlags, PRTime time)
+                uint32_t providerFlags, Time time)
 {
   MOZ_ASSERT(infoObject);
   MOZ_ASSERT(cert);
 
   SECStatus rv;
 
   // We want to avoid storing any intermediate cert information when browsing
   // in private, transient contexts.
@@ -798,29 +805,30 @@ AuthCertificate(CertVerifier& certVerifi
 /*static*/ SECStatus
 SSLServerCertVerificationJob::Dispatch(
   const RefPtr<SharedCertVerifier>& certVerifier,
   const void* fdForLogging,
   TransportSecurityInfo* infoObject,
   CERTCertificate* serverCert,
   SECItem* stapledOCSPResponse,
   uint32_t providerFlags,
-  PRTime time)
+  Time time,
+  PRTime prtime)
 {
   // Runs on the socket transport thread
   if (!certVerifier || !infoObject || !serverCert) {
     NS_ERROR("Invalid parameters for SSL server cert validation");
     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
     return SECFailure;
   }
 
   RefPtr<SSLServerCertVerificationJob> job(
     new SSLServerCertVerificationJob(certVerifier, fdForLogging, infoObject,
                                      serverCert, stapledOCSPResponse,
-                                     providerFlags, time));
+                                     providerFlags, time, prtime));
 
   nsresult nrv;
   if (!gCertVerificationThreadPool) {
     nrv = NS_ERROR_NOT_INITIALIZED;
   } else {
     nrv = gCertVerificationThreadPool->Dispatch(job, NS_DISPATCH_NORMAL);
   }
   if (NS_FAILED(nrv)) {
@@ -884,17 +892,17 @@ SSLServerCertVerificationJob::Run()
       TimeStamp now = TimeStamp::Now();
       MutexAutoLock telemetryMutex(*gSSLVerificationTelemetryMutex);
       Telemetry::AccumulateTimeDelta(failureTelemetry, mJobStartTime, now);
     }
     if (error != 0) {
       RefPtr<CertErrorRunnable> runnable(
           CreateCertErrorRunnable(*mCertVerifier, error, mInfoObject,
                                   mCert.get(), mFdForLogging, mProviderFlags,
-                                  mTime));
+                                  mPRTime));
       if (!runnable) {
         // CreateCertErrorRunnable set a new error code
         error = PR_GetError();
       } else {
         // We must block the the socket transport service thread while the
         // main thread executes the CertErrorRunnable. The CertErrorRunnable
         // will dispatch the result asynchronously, so we don't have to block
         // this thread waiting for it.
@@ -964,19 +972,18 @@ AuthCertificateHook(void* arg, PRFileDes
 
   if (!checkSig || isServer || !socketInfo || !serverCert) {
       PR_SetError(PR_INVALID_STATE_ERROR, 0);
       return SECFailure;
   }
 
   socketInfo->SetFullHandshake();
 
-  // This value of "now" is used both here for OCSP stapling and later
-  // when calling CreateCertErrorRunnable.
-  PRTime now = PR_Now();
+  Time now(Now());
+  PRTime prnow(PR_Now());
 
   if (BlockServerCertChangeForSpdy(socketInfo, serverCert) != SECSuccess)
     return SECFailure;
 
   bool onSTSThread;
   nsresult nrv;
   nsCOMPtr<nsIEventTarget> sts
     = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
@@ -1008,17 +1015,18 @@ AuthCertificateHook(void* arg, PRFileDes
 
     // We *must* do certificate verification on a background thread because
     // we need the socket transport thread to be free for our OCSP requests,
     // and we *want* to do certificate verification on a background thread
     // because of the performance benefits of doing so.
     socketInfo->SetCertVerificationWaiting();
     SECStatus rv = SSLServerCertVerificationJob::Dispatch(
                      certVerifier, static_cast<const void*>(fd), socketInfo,
-                     serverCert, stapledOCSPResponse, providerFlags, now);
+                     serverCert, stapledOCSPResponse, providerFlags, now,
+                     prnow);
     return rv;
   }
 
   // We can't do certificate verification on a background thread, because the
   // thread doing the network I/O may not interrupt its network I/O on receipt
   // of our SSLServerCertVerificationResult event, and/or it might not even be
   // a non-blocking socket.
 
@@ -1029,17 +1037,17 @@ AuthCertificateHook(void* arg, PRFileDes
     return SECSuccess;
   }
 
   PRErrorCode error = PR_GetError();
   if (error != 0) {
     RefPtr<CertErrorRunnable> runnable(
         CreateCertErrorRunnable(*certVerifier, error, socketInfo, serverCert,
                                 static_cast<const void*>(fd), providerFlags,
-                                now));
+                                prnow));
     if (!runnable) {
       // CreateCertErrorRunnable sets a new error code when it fails
       error = PR_GetError();
     } else {
       // We have to return SECSuccess or SECFailure based on the result of the
       // override processing, so we must block this thread waiting for it. The
       // CertErrorRunnable will NOT dispatch the result at all, since we passed
       // false for CreateCertErrorRunnable's async parameter
--- a/security/manager/ssl/src/nsDataSignatureVerifier.cpp
+++ b/security/manager/ssl/src/nsDataSignatureVerifier.cpp
@@ -264,17 +264,17 @@ VerifyCertificate(CERTCertificate* cert,
                                NS_ConvertUTF16toUTF8(orgName),
                                xpcomCert);
 
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
 
   return MapSECStatus(certVerifier->VerifyCert(cert,
                                                certificateUsageObjectSigner,
-                                               PR_Now(), pinArg,
+                                               Now(), pinArg,
                                                nullptr, // hostname
                                                0, // flags
                                                nullptr, // stapledOCSPResponse
                                                &context->builtChain));
 }
 
 } // unnamed namespcae
 
--- a/security/manager/ssl/src/nsNSSCertificate.cpp
+++ b/security/manager/ssl/src/nsNSSCertificate.cpp
@@ -825,24 +825,25 @@ nsNSSCertificate::GetChain(nsIArray** _r
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   NS_ENSURE_ARG(_rvChain);
   nsresult rv;
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Getting chain for \"%s\"\n", mCert->nickname));
 
+  mozilla::pkix::Time now(mozilla::pkix::Now());
+
   ScopedCERTCertList nssChain;
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
 
   // We want to test all usages, but we start with server because most of the
   // time Firefox users care about server certs.
-  if (certVerifier->VerifyCert(mCert.get(),
-                               certificateUsageSSLServer, PR_Now(),
+  if (certVerifier->VerifyCert(mCert.get(), certificateUsageSSLServer, now,
                                nullptr, /*XXX fixme*/
                                nullptr, /* hostname */
                                CertVerifier::FLAG_LOCAL_ONLY,
                                nullptr, /* stapledOCSPResponse */
                                &nssChain) != SECSuccess) {
     nssChain = nullptr;
     // keep going
   }
@@ -859,18 +860,17 @@ nsNSSCertificate::GetChain(nsIArray** _r
        usage < certificateUsageAnyCA && !nssChain;
        usage = usage << 1) {
     if ((usage & otherUsagesToTest) == 0) {
       continue;
     }
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
            ("pipnss: PKIX attempting chain(%d) for '%s'\n",
             usage, mCert->nickname));
-    if (certVerifier->VerifyCert(mCert.get(),
-                                 usage, PR_Now(),
+    if (certVerifier->VerifyCert(mCert.get(), usage, now,
                                  nullptr, /*XXX fixme*/
                                  nullptr, /*hostname*/
                                  CertVerifier::FLAG_LOCAL_ONLY,
                                  nullptr, /* stapledOCSPResponse */
                                  &nssChain) != SECSuccess) {
       nssChain = nullptr;
       // keep going
     }
@@ -1403,17 +1403,17 @@ nsNSSCertificate::hasValidEVOidTag(SECOi
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
 
   validEV = false;
   resultOidTag = SEC_OID_UNKNOWN;
 
   uint32_t flags = mozilla::psm::CertVerifier::FLAG_LOCAL_ONLY |
     mozilla::psm::CertVerifier::FLAG_MUST_BE_EV;
   SECStatus rv = certVerifier->VerifyCert(mCert.get(),
-    certificateUsageSSLServer, PR_Now(),
+    certificateUsageSSLServer, mozilla::pkix::Now(),
     nullptr /* XXX pinarg */,
     nullptr /* hostname */,
     flags, nullptr /* stapledOCSPResponse */ , nullptr, &resultOidTag);
 
   if (rv != SECSuccess) {
     resultOidTag = SEC_OID_UNKNOWN;
   }
   if (resultOidTag != SEC_OID_UNKNOWN) {
--- a/security/manager/ssl/src/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/src/nsNSSCertificateDB.cpp
@@ -566,17 +566,16 @@ nsNSSCertificateDB::ImportEmailCertifica
     PORT_FreeArena(arena, false);
     return NS_ERROR_FAILURE;
   }
 
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
 
   certdb = CERT_GetDefaultCertDB();
-  const PRTime now = PR_Now();
 
   numcerts = certCollection->numcerts;
 
   rawArray = (SECItem **) PORT_Alloc(sizeof(SECItem *) * numcerts);
   if ( !rawArray ) {
     nsrv = NS_ERROR_FAILURE;
     goto loser;
   }
@@ -621,18 +620,18 @@ nsNSSCertificateDB::ImportEmailCertifica
     if (!node->cert) {
       continue;
     }
 
     ScopedCERTCertList certChain;
 
     SECStatus rv = certVerifier->VerifyCert(node->cert,
                                             certificateUsageEmailRecipient,
-                                            now, ctx, nullptr, 0,
-                                            nullptr, &certChain);
+                                            mozilla::pkix::Now(), ctx,
+                                            nullptr, 0, nullptr, &certChain);
 
     if (rv != SECSuccess) {
       nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
       DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, locker);
       continue;
     }
     rv = ImportCertsIntoPermanentStorage(certChain, certUsageEmailRecipient,
                                          false);
@@ -789,18 +788,18 @@ nsNSSCertificateDB::ImportValidCACertsIn
   CERTCertListNode *node;
 
   for (node = CERT_LIST_HEAD(certList);
        !CERT_LIST_END(node,certList);
        node = CERT_LIST_NEXT(node)) {
     ScopedCERTCertList certChain;
     SECStatus rv = certVerifier->VerifyCert(node->cert,
                                             certificateUsageVerifyCA,
-                                            PR_Now(), ctx, nullptr, 0, nullptr,
-                                            &certChain);
+                                            mozilla::pkix::Now(), ctx,
+                                            nullptr, 0, nullptr, &certChain);
     if (rv != SECSuccess) {
       nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
       DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock);
       continue;
     }
 
     rv = ImportCertsIntoPermanentStorage(certChain, certUsageAnyCA, true);
     if (rv != SECSuccess) {
@@ -1370,17 +1369,18 @@ nsNSSCertificateDB::FindCertByEmailAddre
   CERTCertListNode *node;
   // search for a valid certificate
   for (node = CERT_LIST_HEAD(certlist);
        !CERT_LIST_END(node, certlist);
        node = CERT_LIST_NEXT(node)) {
 
     SECStatus srv = certVerifier->VerifyCert(node->cert,
                                              certificateUsageEmailRecipient,
-                                             PR_Now(), nullptr /*XXX pinarg*/,
+                                             mozilla::pkix::Now(),
+                                             nullptr /*XXX pinarg*/,
                                              nullptr /*hostname*/);
     if (srv == SECSuccess) {
       break;
     }
   }
 
   if (CERT_LIST_END(node, certlist)) {
     // no valid cert found
@@ -1738,18 +1738,17 @@ nsNSSCertificateDB::VerifyCertNow(nsIX50
 
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
 
   ScopedCERTCertList resultChain;
   SECOidTag evOidPolicy;
   SECStatus srv;
 
-  srv = certVerifier->VerifyCert(nssCert,
-                                 aUsage, PR_Now(),
+  srv = certVerifier->VerifyCert(nssCert, aUsage, mozilla::pkix::Now(),
                                  nullptr, // Assume no context
                                  nullptr, // hostname
                                  aFlags,
                                  nullptr, // stapledOCSPResponse
                                  &resultChain,
                                  &evOidPolicy);
 
   PRErrorCode error = PR_GetError();
--- a/security/manager/ssl/src/nsUsageArrayHelper.cpp
+++ b/security/manager/ssl/src/nsUsageArrayHelper.cpp
@@ -57,17 +57,17 @@ isFatalError(uint32_t checkResult)
 // usage is appended to outUsages, and nsNSSCertificate::VERIFIED_OK is
 // returned. Otherwise, if validation failed, one of the other "Constants for
 // certificate verification results" in nsIX509Cert is returned.
 uint32_t
 nsUsageArrayHelper::check(uint32_t previousCheckResult,
                           const char *suffix,
                           CertVerifier * certVerifier,
                           SECCertificateUsage aCertUsage,
-                          PRTime time,
+                          mozilla::pkix::Time time,
                           CertVerifier::Flags flags,
                           uint32_t &aCounter,
                           char16_t **outUsages)
 {
   if (!aCertUsage) {
     MOZ_CRASH("caller should have supplied non-zero aCertUsage");
   }
 
@@ -189,17 +189,18 @@ nsUsageArrayHelper::GetUsagesArray(const
     return NS_ERROR_FAILURE;
 
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
 
   uint32_t &count = *_count;
   count = 0;
 
-  PRTime now = PR_Now();
+  mozilla::pkix::Time now(mozilla::pkix::Now());
+
   CertVerifier::Flags flags = localOnly ? CertVerifier::FLAG_LOCAL_ONLY : 0;
 
   // The following list of checks must be < max_returned_out_array_size
 
   uint32_t result;
   result = check(nsIX509Cert::VERIFIED_OK, suffix, certVerifier,
                  certificateUsageSSLClient, now, flags, count, outUsages);
   result = check(result, suffix, certVerifier,
--- a/security/manager/ssl/src/nsUsageArrayHelper.h
+++ b/security/manager/ssl/src/nsUsageArrayHelper.h
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _NSUSAGEARRAYHELPER_H_
 #define _NSUSAGEARRAYHELPER_H_
 
 #include "CertVerifier.h"
 #include "nsNSSComponent.h"
 #include "certt.h"
+#include "pkix/Time.h"
 
 class nsUsageArrayHelper
 {
 public:
   nsUsageArrayHelper(CERTCertificate *aCert);
 
   nsresult GetUsagesArray(const char *suffix,
                bool localOnly,
@@ -28,17 +29,17 @@ private:
   nsresult m_rv;
   CERTCertDBHandle *defaultcertdb;
   nsCOMPtr<nsINSSComponent> nssComponent;
 
   uint32_t check(uint32_t previousCheckResult,
                  const char *suffix,
                  mozilla::psm::CertVerifier * certVerifier,
                  SECCertificateUsage aCertUsage,
-                 PRTime time,
+                 mozilla::pkix::Time time,
                  mozilla::psm::CertVerifier::Flags flags,
                  uint32_t &aCounter,
                  char16_t **outUsages);
 
   void verifyFailed(uint32_t *_verified, int err);
 };
 
 #endif
--- a/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
+++ b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
@@ -17,39 +17,43 @@
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 using namespace mozilla::psm;
 
 const int MaxCacheEntries = 1024;
 
 class OCSPCacheTest : public ::testing::Test
 {
-  protected:
-    static void SetUpTestCase()
-    {
-      NSS_NoDB_Init(nullptr);
-      mozilla::psm::InitCertVerifierLog();
-    }
+protected:
+  OCSPCacheTest() : now(Now()) { }
 
-    mozilla::psm::OCSPCache cache;
+  static void SetUpTestCase()
+  {
+    NSS_NoDB_Init(nullptr);
+    mozilla::psm::InitCertVerifierLog();
+  }
+
+  const Time now;
+  mozilla::psm::OCSPCache cache;
 };
 
 static void
 PutAndGet(OCSPCache& cache, const CertID& certID, Result result,
-          PRTime time)
+          Time time)
 {
   // The first time is thisUpdate. The second is validUntil.
   // The caller is expecting the validUntil returned with Get
   // to be equal to the passed-in time. Since these values will
   // be different in practice, make thisUpdate less than validUntil.
-  ASSERT_TRUE(time >= 10);
-  Result rv = cache.Put(certID, result, time - 10, time);
+  Time thisUpdate(time);
+  ASSERT_EQ(Success, thisUpdate.SubtractSeconds(10));
+  Result rv = cache.Put(certID, result, thisUpdate, time);
   ASSERT_TRUE(rv == Success);
   Result resultOut;
-  PRTime timeOut;
+  Time timeOut(Time::uninitialized);
   ASSERT_TRUE(cache.Get(certID, resultOut, timeOut));
   ASSERT_EQ(result, resultOut);
   ASSERT_EQ(time, timeOut);
 }
 
 TestInput fakeIssuer1("CN=issuer1");
 TestInput fakeKey000("key000");
 TestInput fakeKey001("key001");
@@ -57,209 +61,240 @@ TestInput fakeSerial0000("0000");
 
 TEST_F(OCSPCacheTest, TestPutAndGet)
 {
   TestInput fakeSerial000("000");
   TestInput fakeSerial001("001");
 
   SCOPED_TRACE("");
   PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial001),
-            Success, PR_Now());
+            Success, now);
   Result resultOut;
-  PRTime timeOut;
+  Time timeOut(Time::uninitialized);
   ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial000),
                          resultOut, timeOut));
 }
 
 TEST_F(OCSPCacheTest, TestVariousGets)
 {
   SCOPED_TRACE("");
-  PRTime timeIn = PR_Now();
   for (int i = 0; i < MaxCacheEntries; i++) {
     uint8_t serialBuf[8];
     PR_snprintf(reinterpret_cast<char*>(serialBuf), sizeof(serialBuf), "%04d", i);
     Input fakeSerial;
     ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+    Time timeIn(now);
+    ASSERT_EQ(Success, timeIn.AddSeconds(i));
     PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
-              Success, timeIn + i);
+              Success, timeIn);
   }
 
+  Time timeIn(now);
   Result resultOut;
-  PRTime timeOut;
+  Time timeOut(Time::uninitialized);
 
   // This will be at the end of the list in the cache
   CertID cert0000(fakeIssuer1, fakeKey000, fakeSerial0000);
   ASSERT_TRUE(cache.Get(cert0000, resultOut, timeOut));
   ASSERT_EQ(Success, resultOut);
   ASSERT_EQ(timeIn, timeOut);
   // Once we access it, it goes to the front
   ASSERT_TRUE(cache.Get(cert0000, resultOut, timeOut));
   ASSERT_EQ(Success, resultOut);
   ASSERT_EQ(timeIn, timeOut);
 
   // This will be in the middle
+  Time timeInPlus512(now);
+  ASSERT_EQ(Success, timeInPlus512.AddSeconds(512));
+
   static const TestInput fakeSerial0512("0512");
   CertID cert0512(fakeIssuer1, fakeKey000, fakeSerial0512);
   ASSERT_TRUE(cache.Get(cert0512, resultOut, timeOut));
   ASSERT_EQ(Success, resultOut);
-  ASSERT_EQ(timeIn + 512, timeOut);
+  ASSERT_EQ(timeInPlus512, timeOut);
   ASSERT_TRUE(cache.Get(cert0512, resultOut, timeOut));
   ASSERT_EQ(Success, resultOut);
-  ASSERT_EQ(timeIn + 512, timeOut);
+  ASSERT_EQ(timeInPlus512, timeOut);
 
   // We've never seen this certificate
   static const TestInput fakeSerial1111("1111");
   ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey000, fakeSerial1111),
                          resultOut, timeOut));
 }
 
 TEST_F(OCSPCacheTest, TestEviction)
 {
   SCOPED_TRACE("");
-  PRTime timeIn = PR_Now();
-
   // By putting more distinct entries in the cache than it can hold,
   // we cause the least recently used entry to be evicted.
   for (int i = 0; i < MaxCacheEntries + 1; i++) {
     uint8_t serialBuf[8];
     PR_snprintf(reinterpret_cast<char*>(serialBuf), sizeof(serialBuf), "%04d", i);
     Input fakeSerial;
     ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+    Time timeIn(now);
+    ASSERT_EQ(Success, timeIn.AddSeconds(i));
     PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
-              Success, timeIn + i);
+              Success, timeIn);
   }
 
   Result resultOut;
-  PRTime timeOut;
+  Time timeOut(Time::uninitialized);
   ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial0000),
                          resultOut, timeOut));
 }
 
 TEST_F(OCSPCacheTest, TestNoEvictionForRevokedResponses)
 {
   SCOPED_TRACE("");
-  PRTime timeIn = PR_Now();
   CertID notEvicted(fakeIssuer1, fakeKey000, fakeSerial0000);
+  Time timeIn(now);
   PutAndGet(cache, notEvicted, Result::ERROR_REVOKED_CERTIFICATE, timeIn);
   // By putting more distinct entries in the cache than it can hold,
   // we cause the least recently used entry that isn't revoked to be evicted.
   for (int i = 1; i < MaxCacheEntries + 1; i++) {
     uint8_t serialBuf[8];
     PR_snprintf(reinterpret_cast<char*>(serialBuf), sizeof(serialBuf), "%04d", i);
     Input fakeSerial;
     ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+    Time timeIn(now);
+    ASSERT_EQ(Success, timeIn.AddSeconds(i));
     PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
-              Success, timeIn + i);
+              Success, timeIn);
   }
   Result resultOut;
-  PRTime timeOut;
+  Time timeOut(Time::uninitialized);
   ASSERT_TRUE(cache.Get(notEvicted, resultOut, timeOut));
   ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
   ASSERT_EQ(timeIn, timeOut);
 
   TestInput fakeSerial0001("0001");
   CertID evicted(fakeIssuer1, fakeKey000, fakeSerial0001);
   ASSERT_FALSE(cache.Get(evicted, resultOut, timeOut));
 }
 
 TEST_F(OCSPCacheTest, TestEverythingIsRevoked)
 {
   SCOPED_TRACE("");
-  PRTime timeIn = PR_Now();
+  Time timeIn(now);
   // Fill up the cache with revoked responses.
   for (int i = 0; i < MaxCacheEntries; i++) {
     uint8_t serialBuf[8];
     PR_snprintf(reinterpret_cast<char*>(serialBuf), sizeof(serialBuf), "%04d", i);
     Input fakeSerial;
     ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+    Time timeIn(now);
+    ASSERT_EQ(Success, timeIn.AddSeconds(i));
     PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
-              Result::ERROR_REVOKED_CERTIFICATE, timeIn + i);
+              Result::ERROR_REVOKED_CERTIFICATE, timeIn);
   }
   static const TestInput fakeSerial1025("1025");
   CertID good(fakeIssuer1, fakeKey000, fakeSerial1025);
   // This will "succeed", allowing verification to continue. However,
   // nothing was actually put in the cache.
-  Result result = cache.Put(good, Success, timeIn + 1025 - 50, timeIn + 1025);
+  Time timeInPlus1025(timeIn);
+  ASSERT_EQ(Success, timeInPlus1025.AddSeconds(1025));
+  Time timeInPlus1025Minus50(timeInPlus1025);
+  ASSERT_EQ(Success, timeInPlus1025Minus50.SubtractSeconds(50));
+  Result result = cache.Put(good, Success, timeInPlus1025Minus50,
+                            timeInPlus1025);
   ASSERT_EQ(Success, result);
   Result resultOut;
-  PRTime timeOut;
+  Time timeOut(Time::uninitialized);
   ASSERT_FALSE(cache.Get(good, resultOut, timeOut));
 
   static const TestInput fakeSerial1026("1026");
   CertID revoked(fakeIssuer1, fakeKey000, fakeSerial1026);
   // This will fail, causing verification to fail.
+  Time timeInPlus1026(timeIn);
+  ASSERT_EQ(Success, timeInPlus1026.AddSeconds(1026));
+  Time timeInPlus1026Minus50(timeInPlus1026);
+  ASSERT_EQ(Success, timeInPlus1026Minus50.SubtractSeconds(50));
   result = cache.Put(revoked, Result::ERROR_REVOKED_CERTIFICATE,
-                     timeIn + 1026 - 50, timeIn + 1026);
+                     timeInPlus1026Minus50, timeInPlus1026);
   ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, result);
 }
 
 TEST_F(OCSPCacheTest, VariousIssuers)
 {
   SCOPED_TRACE("");
+  Time timeIn(now);
   static const TestInput fakeIssuer2("CN=issuer2");
   static const TestInput fakeSerial001("001");
-  PRTime timeIn = PR_Now();
   CertID subject(fakeIssuer1, fakeKey000, fakeSerial001);
-  PutAndGet(cache, subject, Success, timeIn);
+  PutAndGet(cache, subject, Success, now);
   Result resultOut;
-  PRTime timeOut;
+  Time timeOut(Time::uninitialized);
   ASSERT_TRUE(cache.Get(subject, resultOut, timeOut));
   ASSERT_EQ(Success, resultOut);
   ASSERT_EQ(timeIn, timeOut);
   // Test that we don't match a different issuer DN
   ASSERT_FALSE(cache.Get(CertID(fakeIssuer2, fakeKey000, fakeSerial001),
                          resultOut, timeOut));
   // Test that we don't match a different issuer key
   ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial001),
                          resultOut, timeOut));
 }
 
 TEST_F(OCSPCacheTest, Times)
 {
   SCOPED_TRACE("");
   CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
-  PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT, 100);
-  PutAndGet(cache, certID, Success, 200);
+  PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT,
+            TimeFromElapsedSecondsAD(100));
+  PutAndGet(cache, certID, Success, TimeFromElapsedSecondsAD(200));
   // This should not override the more recent entry.
   ASSERT_EQ(Success,
-            cache.Put(certID, Result::ERROR_OCSP_UNKNOWN_CERT, 100, 100));
+            cache.Put(certID, Result::ERROR_OCSP_UNKNOWN_CERT,
+                      TimeFromElapsedSecondsAD(100),
+                      TimeFromElapsedSecondsAD(100)));
   Result resultOut;
-  PRTime timeOut;
+  Time timeOut(Time::uninitialized);
   ASSERT_TRUE(cache.Get(certID, resultOut, timeOut));
   // Here we see the more recent time.
   ASSERT_EQ(Success, resultOut);
-  ASSERT_EQ(200, timeOut);
+  ASSERT_EQ(TimeFromElapsedSecondsAD(200), timeOut);
 
   // Result::ERROR_REVOKED_CERTIFICATE overrides everything
-  PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE, 50);
+  PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE,
+            TimeFromElapsedSecondsAD(50));
 }
 
 TEST_F(OCSPCacheTest, NetworkFailure)
 {
   SCOPED_TRACE("");
   CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
-  PutAndGet(cache, certID, Result::ERROR_CONNECT_REFUSED, 100);
-  PutAndGet(cache, certID, Success, 200);
+  PutAndGet(cache, certID, Result::ERROR_CONNECT_REFUSED,
+            TimeFromElapsedSecondsAD(100));
+  PutAndGet(cache, certID, Success, TimeFromElapsedSecondsAD(200));
   // This should not override the already present entry.
   ASSERT_EQ(Success,
-            cache.Put(certID, Result::ERROR_CONNECT_REFUSED, 300, 350));
+            cache.Put(certID, Result::ERROR_CONNECT_REFUSED,
+                      TimeFromElapsedSecondsAD(300),
+                      TimeFromElapsedSecondsAD(350)));
   Result resultOut;
-  PRTime timeOut;
+  Time timeOut(Time::uninitialized);
   ASSERT_TRUE(cache.Get(certID, resultOut, timeOut));
   ASSERT_EQ(Success, resultOut);
-  ASSERT_EQ(200, timeOut);
+  ASSERT_EQ(TimeFromElapsedSecondsAD(200), timeOut);
 
-  PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT, 400);
+  PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT,
+            TimeFromElapsedSecondsAD(400));
   // This should not override the already present entry.
   ASSERT_EQ(Success,
-            cache.Put(certID, Result::ERROR_CONNECT_REFUSED, 500, 550));
+            cache.Put(certID, Result::ERROR_CONNECT_REFUSED,
+                      TimeFromElapsedSecondsAD(500),
+                      TimeFromElapsedSecondsAD(550)));
   ASSERT_TRUE(cache.Get(certID, resultOut, timeOut));
   ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT, resultOut);
-  ASSERT_EQ(400, timeOut);
+  ASSERT_EQ(TimeFromElapsedSecondsAD(400), timeOut);
 
-  PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE, 600);
+  PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE,
+            TimeFromElapsedSecondsAD(600));
   // This should not override the already present entry.
   ASSERT_EQ(Success,
-            cache.Put(certID, Result::ERROR_CONNECT_REFUSED, 700, 750));
+            cache.Put(certID, Result::ERROR_CONNECT_REFUSED,
+                      TimeFromElapsedSecondsAD(700),
+                      TimeFromElapsedSecondsAD(750)));
   ASSERT_TRUE(cache.Get(certID, resultOut, timeOut));
   ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
-  ASSERT_EQ(600, timeOut);
+  ASSERT_EQ(TimeFromElapsedSecondsAD(600), timeOut);
 }
new file mode 100644
--- /dev/null
+++ b/security/pkix/include/pkix/Time.h
@@ -0,0 +1,123 @@
+/* -*- 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 2014 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.
+ */
+
+#ifndef mozilla_pkix__Time_h
+#define mozilla_pkix__Time_h
+
+#include <ctime>
+#include <limits>
+#include <stdint.h>
+
+#include "pkix/Result.h"
+
+namespace mozilla { namespace pkix {
+
+// Time with a range from the first second of year 0 (AD) through at least the
+// last second of year 9999, which is the range of legal times in X.509 and
+// OCSP. This type has second-level precision. The time zone is always UTC.
+//
+// Pass by value, not by reference.
+class Time
+{
+public:
+  // Construct an uninitilized instance.
+  //
+  // This will fail to compile because there is no default constructor:
+  //    Time x;
+  //
+  // This will succeed, leaving the time uninitialized:
+  //    Time x(Time::uninitialized);
+  enum Uninitialized { uninitialized };
+  Time(Uninitialized) { }
+
+  bool operator==(const Time& other) const
+  {
+    return elapsedSecondsAD == other.elapsedSecondsAD;
+  }
+  bool operator>(const Time& other) const
+  {
+    return elapsedSecondsAD > other.elapsedSecondsAD;
+  }
+  bool operator>=(const Time& other) const
+  {
+    return elapsedSecondsAD >= other.elapsedSecondsAD;
+  }
+  bool operator<(const Time& other) const
+  {
+    return elapsedSecondsAD < other.elapsedSecondsAD;
+  }
+  bool operator<=(const Time& other) const
+  {
+    return elapsedSecondsAD <= other.elapsedSecondsAD;
+  }
+
+  Result AddSeconds(uint64_t seconds)
+  {
+    if (std::numeric_limits<uint64_t>::max() - elapsedSecondsAD
+          < seconds) {
+      return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow
+    }
+    elapsedSecondsAD += seconds;
+    return Success;
+  }
+
+  Result SubtractSeconds(uint64_t seconds)
+  {
+    if (seconds > elapsedSecondsAD) {
+      return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow
+    }
+    elapsedSecondsAD -= seconds;
+    return Success;
+  }
+
+  static const uint64_t ONE_DAY_IN_SECONDS
+    = UINT64_C(24) * UINT64_C(60) * UINT64_C(60);
+
+private:
+  // This constructor is hidden to prevent accidents like this:
+  //
+  //    Time foo(time_t t)
+  //    {
+  //      // WRONG! 1970-01-01-00:00:00 == time_t(0), but not Time(0)!
+  //      return Time(t);
+  //    }
+  explicit Time(uint64_t elapsedSecondsAD)
+    : elapsedSecondsAD(elapsedSecondsAD)
+  {
+  }
+  friend Time TimeFromElapsedSecondsAD(uint64_t);
+
+  uint64_t elapsedSecondsAD;
+};
+
+inline Time TimeFromElapsedSecondsAD(uint64_t elapsedSecondsAD)
+{
+  return Time(elapsedSecondsAD);
+}
+
+Time Now();
+
+} } // namespace mozilla::pkix
+
+#endif // mozilla_pkix__Time_h
--- a/security/pkix/include/pkix/pkix.h
+++ b/security/pkix/include/pkix/pkix.h
@@ -84,17 +84,17 @@ namespace mozilla { namespace pkix {
 //
 // Result::ERROR_UNTRUSTED_CERT means that the end-entity certificate was
 //                              actively distrusted.
 // Result::SEC_ERROR_UNTRUSTED_ISSUER means that path building failed because
 //                                    of active distrust.
 // TODO(bug 968451): Document more of these.
 
 Result BuildCertChain(TrustDomain& trustDomain, Input cert,
-                      PRTime time, EndEntityOrCA endEntityOrCA,
+                      Time time, EndEntityOrCA endEntityOrCA,
                       KeyUsage requiredKeyUsageIfPresent,
                       KeyPurposeId requiredEKUIfPresent,
                       const CertPolicyId& requiredPolicy,
                       /*optional*/ const Input* stapledOCSPResponse);
 
 static const size_t OCSP_REQUEST_MAX_LENGTH = 127;
 Result CreateEncodedOCSPRequest(TrustDomain& trustDomain,
                                 const struct CertID& certID,
@@ -109,18 +109,18 @@ Result CreateEncodedOCSPRequest(TrustDom
 // The optional parameter thisUpdate will be the thisUpdate value of
 // the encoded response if it is considered trustworthy. Only
 // good, unknown, or revoked responses that verify correctly are considered
 // trustworthy. If the response is not trustworthy, thisUpdate will be 0.
 // Similarly, the optional parameter validThrough will be the time through
 // which the encoded response is considered trustworthy (that is, if a response had a
 // thisUpdate time of validThrough, it would be considered trustworthy).
 Result VerifyEncodedOCSPResponse(TrustDomain& trustDomain,
-                                 const CertID& certID, PRTime time,
+                                 const CertID& certID, Time time,
                                  uint16_t maxLifetimeInDays,
                                  Input encodedResponse,
                        /* out */ bool& expired,
-              /* optional out */ PRTime* thisUpdate = nullptr,
-              /* optional out */ PRTime* validThrough = nullptr);
+              /* optional out */ Time* thisUpdate = nullptr,
+              /* optional out */ Time* validThrough = nullptr);
 
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkix_h
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -21,17 +21,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef mozilla_pkix__pkixtypes_h
 #define mozilla_pkix__pkixtypes_h
 
 #include "pkix/Input.h"
-#include "prtime.h"
+#include "pkix/Time.h"
 #include "stdint.h"
 
 namespace mozilla { namespace pkix {
 
 MOZILLA_PKIX_ENUM_CLASS DigestAlgorithm
 {
   sha512 = 1,
   sha384 = 2,
@@ -254,17 +254,17 @@ public:
   //                TrustDomain::FindIssuer
   //                  [...]
   //                    IssuerChecker::Check
   //                      [...]
   //
   // checker.Check is responsible for limiting the recursion to a reasonable
   // limit.
   virtual Result FindIssuer(Input encodedIssuerName,
-                            IssuerChecker& checker, PRTime time) = 0;
+                            IssuerChecker& checker, Time time) = 0;
 
   // Called as soon as we think we have a valid chain but before revocation
   // checks are done. This function can be used to compute additional checks,
   // especilaly checks that require the entire certificate chain. This callback
   // can also be used to save a copy of the built certificate chain for later
   // use.
   //
   // This function may be called multiple times, regardless of whether it
@@ -282,17 +282,17 @@ public:
   // very wrong to assume that the certificate chain is valid.
   //
   // certChain.GetDER(0) is the trust anchor.
   virtual Result IsChainValid(const DERArray& certChain) = 0;
 
   // issuerCertToDup is only non-const so CERT_DupCertificate can be called on
   // it.
   virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA,
-                                 const CertID& certID, PRTime time,
+                                 const CertID& certID, Time time,
                     /*optional*/ const Input* stapledOCSPresponse,
                     /*optional*/ const Input* aiaExtension) = 0;
 
   // Check that the key size, algorithm, and parameters of the given public key
   // are acceptable.
   //
   // VerifySignedData() should do the same checks that this function does, but
   // mainly for efficiency, some keys are not passed to VerifySignedData().
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -19,42 +19,40 @@
  * 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 "pkix/pkix.h"
 
-#include <limits>
-
 #include "pkixcheck.h"
 #include "pkixutil.h"
 
 namespace mozilla { namespace pkix {
 
 static Result BuildForward(TrustDomain& trustDomain,
                            const BackCert& subject,
-                           PRTime time,
+                           Time time,
                            KeyUsage requiredKeyUsageIfPresent,
                            KeyPurposeId requiredEKUIfPresent,
                            const CertPolicyId& requiredPolicy,
                            /*optional*/ const Input* stapledOCSPResponse,
                            unsigned int subCACount);
 
 TrustDomain::IssuerChecker::IssuerChecker() { }
 TrustDomain::IssuerChecker::~IssuerChecker() { }
 
 // The implementation of TrustDomain::IssuerTracker is in a subclass only to
 // hide the implementation from external users.
 class PathBuildingStep : public TrustDomain::IssuerChecker
 {
 public:
   PathBuildingStep(TrustDomain& trustDomain, const BackCert& subject,
-                   PRTime time, KeyPurposeId requiredEKUIfPresent,
+                   Time time, KeyPurposeId requiredEKUIfPresent,
                    const CertPolicyId& requiredPolicy,
                    /*optional*/ const Input* stapledOCSPResponse,
                    unsigned int subCACount)
     : trustDomain(trustDomain)
     , subject(subject)
     , time(time)
     , requiredEKUIfPresent(requiredEKUIfPresent)
     , requiredPolicy(requiredPolicy)
@@ -69,17 +67,17 @@ public:
                /*optional*/ const Input* additionalNameConstraints,
                /*out*/ bool& keepGoing);
 
   Result CheckResult() const;
 
 private:
   TrustDomain& trustDomain;
   const BackCert& subject;
-  const PRTime time;
+  const Time time;
   const KeyPurposeId requiredEKUIfPresent;
   const CertPolicyId& requiredPolicy;
   /*optional*/ Input const* const stapledOCSPResponse;
   const unsigned int subCACount;
 
   Result RecordResult(Result currentResult, /*out*/ bool& keepGoing);
   Result result;
   bool resultWasSet;
@@ -203,17 +201,17 @@ PathBuildingStep::Check(Input potentialI
 //
 // Be very careful about changing the order of checks. The order is significant
 // because it affects which error we return when a certificate or certificate
 // chain has multiple problems. See the error ranking documentation in
 // pkix/pkix.h.
 static Result
 BuildForward(TrustDomain& trustDomain,
              const BackCert& subject,
-             PRTime time,
+             Time time,
              KeyUsage requiredKeyUsageIfPresent,
              KeyPurposeId requiredEKUIfPresent,
              const CertPolicyId& requiredPolicy,
              /*optional*/ const Input* stapledOCSPResponse,
              unsigned int subCACount)
 {
   Result rv;
 
@@ -291,17 +289,17 @@ BuildForward(TrustDomain& trustDomain,
   }
 
   // We've built a valid chain from the subject cert up to a trusted root.
   return Success;
 }
 
 Result
 BuildCertChain(TrustDomain& trustDomain, Input certDER,
-               PRTime time, EndEntityOrCA endEntityOrCA,
+               Time time, EndEntityOrCA endEntityOrCA,
                KeyUsage requiredKeyUsageIfPresent,
                KeyPurposeId requiredEKUIfPresent,
                const CertPolicyId& requiredPolicy,
                /*optional*/ const Input* stapledOCSPResponse)
 {
   // XXX: Support the legacy use of the subject CN field for indicating the
   // domain name the certificate is valid for.
   BackCert cert(certDER, endEntityOrCA, nullptr);
--- a/security/pkix/lib/pkixcheck.cpp
+++ b/security/pkix/lib/pkixcheck.cpp
@@ -30,28 +30,28 @@
 #include "pkix/ScopedPtr.h"
 #include "pkixder.h"
 #include "pkix/pkixnss.h"
 #include "pkixutil.h"
 
 namespace mozilla { namespace pkix {
 
 Result
-CheckValidity(Input encodedValidity, PRTime time)
+CheckValidity(Input encodedValidity, Time time)
 {
   Reader validity(encodedValidity);
-  PRTime notBefore;
+  Time notBefore(Time::uninitialized);
   if (der::TimeChoice(validity, notBefore) != Success) {
     return Result::ERROR_EXPIRED_CERTIFICATE;
   }
   if (time < notBefore) {
     return Result::ERROR_EXPIRED_CERTIFICATE;
   }
 
-  PRTime notAfter;
+  Time notAfter(Time::uninitialized);
   if (der::TimeChoice(validity, notAfter) != Success) {
     return Result::ERROR_EXPIRED_CERTIFICATE;
   }
   if (time > notAfter) {
     return Result::ERROR_EXPIRED_CERTIFICATE;
   }
 
   return der::End(validity);
@@ -613,17 +613,17 @@ CheckExtendedKeyUsage(EndEntityOrCA endE
   }
 
   return Success;
 }
 
 Result
 CheckIssuerIndependentProperties(TrustDomain& trustDomain,
                                  const BackCert& cert,
-                                 PRTime time,
+                                 Time time,
                                  KeyUsage requiredKeyUsageIfPresent,
                                  KeyPurposeId requiredEKUIfPresent,
                                  const CertPolicyId& requiredPolicy,
                                  unsigned int subCACount,
                                  /*out*/ TrustLevel& trustLevel)
 {
   Result rv;
 
--- a/security/pkix/lib/pkixcheck.h
+++ b/security/pkix/lib/pkixcheck.h
@@ -29,17 +29,17 @@
 
 namespace mozilla { namespace pkix {
 
 class BackCert;
 
 Result CheckIssuerIndependentProperties(
           TrustDomain& trustDomain,
           const BackCert& cert,
-          PRTime time,
+          Time time,
           KeyUsage requiredKeyUsageIfPresent,
           KeyPurposeId requiredEKUIfPresent,
           const CertPolicyId& requiredPolicy,
           unsigned int subCACount,
           /*out*/ TrustLevel& trustLevel);
 
 Result CheckNameConstraints(Input encodedNameConstraints,
                             const BackCert& firstChild,
--- a/security/pkix/lib/pkixder.cpp
+++ b/security/pkix/lib/pkixder.cpp
@@ -18,18 +18,19 @@
  * 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 "pkixder.h"
+
 #include "pkix/bind.h"
-#include "cert.h"
+#include "pkixutil.h"
 
 namespace mozilla { namespace pkix { namespace der {
 
 namespace internal {
 
 // Too complicated to be inline
 Result
 ExpectTagAndGetLength(Reader& input, uint8_t expectedTag, uint16_t& length)
@@ -330,133 +331,117 @@ BitStringWithNoUnusedBits(Reader& input,
     return Result::ERROR_BAD_DER;
   }
   Reader::Mark mark(valueWithUnusedBits.GetMark());
   valueWithUnusedBits.SkipToEnd();
   return valueWithUnusedBits.GetInput(mark, value);
 }
 
 static inline Result
-ReadDigit(Reader& input, /*out*/ int& value)
+ReadDigit(Reader& input, /*out*/ unsigned int& value)
 {
   uint8_t b;
   if (input.Read(b) != Success) {
     return Result::ERROR_INVALID_TIME;
   }
   if (b < '0' || b > '9') {
     return Result::ERROR_INVALID_TIME;
   }
-  value = b - '0';
+  value = static_cast<unsigned int>(b - static_cast<uint8_t>('0'));
   return Success;
 }
 
 static inline Result
-ReadTwoDigits(Reader& input, int minValue, int maxValue, /*out*/ int& value)
+ReadTwoDigits(Reader& input, unsigned int minValue, unsigned int maxValue,
+              /*out*/ unsigned int& value)
 {
-  int hi;
+  unsigned int hi;
   Result rv = ReadDigit(input, hi);
   if (rv != Success) {
     return rv;
   }
-  int lo;
+  unsigned int lo;
   rv = ReadDigit(input, lo);
   if (rv != Success) {
     return rv;
   }
   value = (hi * 10) + lo;
   if (value < minValue || value > maxValue) {
     return Result::ERROR_INVALID_TIME;
   }
   return Success;
 }
 
-inline int
-daysBeforeYear(int year)
-{
-  return (365 * (year - 1))
-       + ((year - 1) / 4)    // leap years are every 4 years,
-       - ((year - 1) / 100)  // except years divisible by 100,
-       + ((year - 1) / 400); // except years divisible by 400.
-}
-
 namespace internal {
 
 // We parse GeneralizedTime and UTCTime according to RFC 5280 and we do not
 // accept all time formats allowed in the ASN.1 spec. That is,
 // GeneralizedTime must always be in the format YYYYMMDDHHMMSSZ and UTCTime
 // must always be in the format YYMMDDHHMMSSZ. Timezone formats of the form
 // +HH:MM or -HH:MM or NOT accepted.
 Result
-TimeChoice(Reader& tagged, uint8_t expectedTag, /*out*/ PRTime& time)
+TimeChoice(Reader& tagged, uint8_t expectedTag, /*out*/ Time& time)
 {
-  int days;
+  unsigned int days;
 
   Reader input;
   Result rv = ExpectTagAndGetValue(tagged, expectedTag, input);
   if (rv != Success) {
     return rv;
   }
 
-  int yearHi;
-  int yearLo;
+  unsigned int yearHi;
+  unsigned int yearLo;
   if (expectedTag == GENERALIZED_TIME) {
     rv = ReadTwoDigits(input, 0, 99, yearHi);
     if (rv != Success) {
       return rv;
     }
     rv = ReadTwoDigits(input, 0, 99, yearLo);
     if (rv != Success) {
       return rv;
     }
   } else if (expectedTag == UTCTime) {
     rv = ReadTwoDigits(input, 0, 99, yearLo);
     if (rv != Success) {
       return rv;
     }
-    yearHi = yearLo >= 50 ? 19 : 20;
+    yearHi = yearLo >= 50u ? 19u : 20u;
   } else {
     PR_NOT_REACHED("invalid tag given to TimeChoice");
     return Result::ERROR_INVALID_TIME;
   }
-  int year = (yearHi * 100) + yearLo;
-  if (year < 1970) {
+  unsigned int year = (yearHi * 100u) + yearLo;
+  if (year < 1970u) {
     // We don't support dates before January 1, 1970 because that is the epoch.
     return Result::ERROR_INVALID_TIME;
   }
-  if (year > 1970) {
-    // This is NOT equivalent to daysBeforeYear(year - 1970) because the
-    // leap year calculations in daysBeforeYear only works on absolute years.
-    days = daysBeforeYear(year) - daysBeforeYear(1970);
-    // We subtract 1 because we're interested in knowing how many days there
-    // were *before* the given year, relative to 1970.
-  } else {
-    days = 0;
-  }
+  days = DaysBeforeYear(year);
 
-  int month;
-  rv = ReadTwoDigits(input, 1, 12, month);
+  unsigned int month;
+  rv = ReadTwoDigits(input, 1u, 12u, month);
   if (rv != Success) {
     return rv;
   }
-  int daysInMonth;
-  static const int jan = 31;
-  const int feb = ((year % 4 == 0) &&
-                   ((year % 100 != 0) || (year % 400 == 0)))
-                ? 29
-                : 28;
-  static const int mar = 31;
-  static const int apr = 30;
-  static const int may = 31;
-  static const int jun = 30;
-  static const int jul = 31;
-  static const int aug = 31;
-  static const int sep = 30;
-  static const int oct = 31;
-  static const int nov = 30;
-  static const int dec = 31;
+  unsigned int daysInMonth;
+  static const unsigned int jan = 31u;
+  const unsigned int feb = ((year % 4u == 0u) &&
+                           ((year % 100u != 0u) || (year % 400u == 0u)))
+                         ? 29u
+                         : 28u;
+  static const unsigned int mar = 31u;
+  static const unsigned int apr = 30u;
+  static const unsigned int may = 31u;
+  static const unsigned int jun = 30u;
+  static const unsigned int jul = 31u;
+  static const unsigned int aug = 31u;
+  static const unsigned int sep = 30u;
+  static const unsigned int oct = 31u;
+  static const unsigned int nov = 30u;
+  static const unsigned int dec = 31u;
   switch (month) {
     case 1:  daysInMonth = jan; break;
     case 2:  daysInMonth = feb; days += jan; break;
     case 3:  daysInMonth = mar; days += jan + feb; break;
     case 4:  daysInMonth = apr; days += jan + feb + mar; break;
     case 5:  daysInMonth = may; days += jan + feb + mar + apr; break;
     case 6:  daysInMonth = jun; days += jan + feb + mar + apr + may; break;
     case 7:  daysInMonth = jul; days += jan + feb + mar + apr + may + jun;
@@ -476,54 +461,54 @@ TimeChoice(Reader& tagged, uint8_t expec
     case 12: daysInMonth = dec; days += jan + feb + mar + apr + may + jun +
                                         jul + aug + sep + oct + nov;
              break;
     default:
       PR_NOT_REACHED("month already bounds-checked by ReadTwoDigits");
       return Result::FATAL_ERROR_INVALID_STATE;
   }
 
-  int dayOfMonth;
-  rv = ReadTwoDigits(input, 1, daysInMonth, dayOfMonth);
+  unsigned int dayOfMonth;
+  rv = ReadTwoDigits(input, 1u, daysInMonth, dayOfMonth);
   if (rv != Success) {
     return rv;
   }
   days += dayOfMonth - 1;
 
-  int hours;
-  rv = ReadTwoDigits(input, 0, 23, hours);
+  unsigned int hours;
+  rv = ReadTwoDigits(input, 0u, 23u, hours);
   if (rv != Success) {
     return rv;
   }
-  int minutes;
-  rv = ReadTwoDigits(input, 0, 59, minutes);
+  unsigned int minutes;
+  rv = ReadTwoDigits(input, 0u, 59u, minutes);
   if (rv != Success) {
     return rv;
   }
-  int seconds;
-  rv = ReadTwoDigits(input, 0, 59, seconds);
+  unsigned int seconds;
+  rv = ReadTwoDigits(input, 0u, 59u, seconds);
   if (rv != Success) {
     return rv;
   }
 
   uint8_t b;
   if (input.Read(b) != Success) {
     return Result::ERROR_INVALID_TIME;
   }
   if (b != 'Z') {
     return Result::ERROR_INVALID_TIME;
   }
   if (End(input) != Success) {
     return Result::ERROR_INVALID_TIME;
   }
 
-  int64_t totalSeconds = (static_cast<int64_t>(days) * 24 * 60 * 60) +
-                         (static_cast<int64_t>(hours)     * 60 * 60) +
-                         (static_cast<int64_t>(minutes)        * 60) +
-                         seconds;
+  uint64_t totalSeconds = (static_cast<uint64_t>(days) * 24u * 60u * 60u) +
+                          (static_cast<uint64_t>(hours)      * 60u * 60u) +
+                          (static_cast<uint64_t>(minutes)          * 60u) +
+                          seconds;
 
-  time = totalSeconds * PR_USEC_PER_SEC;
+  time = TimeFromElapsedSecondsAD(totalSeconds);
   return Success;
 }
 
 } // namespace internal
 
 } } } // namespace mozilla::pkix::der
--- a/security/pkix/lib/pkixder.h
+++ b/security/pkix/lib/pkixder.h
@@ -34,17 +34,16 @@
 // input does not match the criteria.
 //
 // Skip* functions unconditionally advance the input mark and return Success if
 // they are able to do so; otherwise they fail with the input mark in an
 // undefined state.
 
 #include "pkix/Input.h"
 #include "pkix/pkixtypes.h"
-#include "prtime.h"
 
 namespace mozilla { namespace pkix { namespace der {
 
 enum Class
 {
    UNIVERSAL = 0 << 6,
 // APPLICATION = 1 << 6, // unused
    CONTEXT_SPECIFIC = 2 << 6,
@@ -328,34 +327,34 @@ Enumerated(Reader& input, uint8_t& value
 namespace internal {
 
 // internal::TimeChoice implements the shared functionality of GeneralizedTime
 // and TimeChoice. tag must be either UTCTime or GENERALIZED_TIME.
 //
 // Only times from 1970-01-01-00:00:00 onward are accepted, in order to
 // eliminate the chance for complications in converting times to traditional
 // time formats that start at 1970.
-Result TimeChoice(Reader& input, uint8_t tag, /*out*/ PRTime& time);
+Result TimeChoice(Reader& input, uint8_t tag, /*out*/ Time& time);
 
 } // namespace internal
 
 // Only times from 1970-01-01-00:00:00 onward are accepted, in order to
 // eliminate the chance for complications in converting times to traditional
 // time formats that start at 1970.
 inline Result
-GeneralizedTime(Reader& input, /*out*/ PRTime& time)
+GeneralizedTime(Reader& input, /*out*/ Time& time)
 {
   return internal::TimeChoice(input, GENERALIZED_TIME, time);
 }
 
 // Only times from 1970-01-01-00:00:00 onward are accepted, in order to
 // eliminate the chance for complications in converting times to traditional
 // time formats that start at 1970.
 inline Result
-TimeChoice(Reader& input, /*out*/ PRTime& time)
+TimeChoice(Reader& input, /*out*/ Time& time)
 {
   uint8_t expectedTag = input.Peek(UTCTime) ? UTCTime : GENERALIZED_TIME;
   return internal::TimeChoice(input, expectedTag, time);
 }
 
 // This parser will only parse values between 0..127. If this range is
 // increased then callers will need to be changed.
 inline Result
--- a/security/pkix/lib/pkixocsp.cpp
+++ b/security/pkix/lib/pkixocsp.cpp
@@ -27,72 +27,68 @@
 #include "pkix/bind.h"
 #include "pkix/pkix.h"
 #include "pkixcheck.h"
 #include "pkixutil.h"
 #include "pkixder.h"
 
 namespace mozilla { namespace pkix {
 
-static const PRTime ONE_DAY
-  = INT64_C(24) * INT64_C(60) * INT64_C(60) * PR_USEC_PER_SEC;
-static const PRTime SLOP = ONE_DAY;
-
 // These values correspond to the tag values in the ASN.1 CertStatus
 MOZILLA_PKIX_ENUM_CLASS CertStatus : uint8_t {
   Good = der::CONTEXT_SPECIFIC | 0,
   Revoked = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
   Unknown = der::CONTEXT_SPECIFIC | 2
 };
 
 class Context
 {
 public:
-  Context(TrustDomain& trustDomain, const CertID& certID, PRTime time,
-          uint16_t maxLifetimeInDays, /*optional out*/ PRTime* thisUpdate,
-          /*optional out*/ PRTime* validThrough)
+  Context(TrustDomain& trustDomain, const CertID& certID, Time time,
+          uint16_t maxLifetimeInDays, /*optional out*/ Time* thisUpdate,
+          /*optional out*/ Time* validThrough)
     : trustDomain(trustDomain)
     , certID(certID)
     , time(time)
     , maxLifetimeInDays(maxLifetimeInDays)
     , certStatus(CertStatus::Unknown)
     , thisUpdate(thisUpdate)
     , validThrough(validThrough)
     , expired(false)
   {
     if (thisUpdate) {
-      *thisUpdate = 0;
+      *thisUpdate = TimeFromElapsedSecondsAD(0);
     }
     if (validThrough) {
-      *validThrough = 0;
+      *validThrough = TimeFromElapsedSecondsAD(0);
     }
   }
 
   TrustDomain& trustDomain;
   const CertID& certID;
-  const PRTime time;
+  const Time time;
   const uint16_t maxLifetimeInDays;
   CertStatus certStatus;
-  PRTime* thisUpdate;
-  PRTime* validThrough;
+  Time* thisUpdate;
+  Time* validThrough;
   bool expired;
 
 private:
   Context(const Context&); // delete
   void operator=(const Context&); // delete
 };
 
 // Verify that potentialSigner is a valid delegated OCSP response signing cert
 // according to RFC 6960 section 4.2.2.2.
 static Result
 CheckOCSPResponseSignerCert(TrustDomain& trustDomain,
                             BackCert& potentialSigner,
                             Input issuerSubject,
                             Input issuerSubjectPublicKeyInfo,
-                            PRTime time)
+                            Time time)
 {
   Result rv;
 
   // We don't need to do a complete verification of the signer (i.e. we don't
   // have to call BuildCertChain to verify the entire chain) because we
   // already know that the issuer is valid, since revocation checking is done
   // from the root to the parent after we've built a complete chain that we
   // know is otherwise valid. Rather, we just need to do a one-step validation
@@ -285,21 +281,21 @@ MapBadDERToMalformedOCSPResponse(Result 
   if (rv == Result::ERROR_BAD_DER) {
     return Result::ERROR_OCSP_MALFORMED_RESPONSE;
   }
   return rv;
 }
 
 Result
 VerifyEncodedOCSPResponse(TrustDomain& trustDomain, const struct CertID& certID,
-                          PRTime time, uint16_t maxOCSPLifetimeInDays,
+                          Time time, uint16_t maxOCSPLifetimeInDays,
                           Input encodedResponse,
                           /*out*/ bool& expired,
-                          /*optional out*/ PRTime* thisUpdate,
-                          /*optional out*/ PRTime* validThrough)
+                          /*optional out*/ Time* thisUpdate,
+                          /*optional out*/ Time* validThrough)
 {
   // Always initialize this to something reasonable.
   expired = false;
 
   Context context(trustDomain, certID, time, maxOCSPLifetimeInDays,
                   thisUpdate, validThrough);
 
   Reader input(encodedResponse);
@@ -492,17 +488,17 @@ ResponseData(Reader& input, Context& con
   // before verifying its signature.
   rv = VerifySignature(context, responderIDType, responderID, certs,
                        signedResponseData);
   if (rv != Success) {
     return rv;
   }
 
   // TODO: Do we even need to parse this? Should we just skip it?
-  PRTime producedAt;
+  Time producedAt(Time::uninitialized);
   rv = der::GeneralizedTime(input, producedAt);
   if (rv != Success) {
     return rv;
   }
 
   // We don't accept an empty sequence of responses. In practice, a legit OCSP
   // responder will never return an empty response, and handling the case of an
   // empty response makes things unnecessarily complicated.
@@ -586,59 +582,74 @@ SingleResponse(Reader& input, Context& c
 
   // http://tools.ietf.org/html/rfc6960#section-3.2
   // 5. The time at which the status being indicated is known to be
   //    correct (thisUpdate) is sufficiently recent;
   // 6. When available, the time at or before which newer information will
   //    be available about the status of the certificate (nextUpdate) is
   //    greater than the current time.
 
-  const PRTime maxLifetime =
-    context.maxLifetimeInDays * ONE_DAY;
-
-  PRTime thisUpdate;
+  Time thisUpdate(Time::uninitialized);
   rv = der::GeneralizedTime(input, thisUpdate);
   if (rv != Success) {
     return rv;
   }
 
-  if (thisUpdate > context.time + SLOP) {
+  static const uint64_t SLOP_SECONDS = Time::ONE_DAY_IN_SECONDS;
+
+  Time timePlusSlop(context.time);
+  rv = timePlusSlop.AddSeconds(SLOP_SECONDS);
+  if (rv != Success) {
+    return rv;
+  }
+  if (thisUpdate > timePlusSlop) {
     return Result::ERROR_OCSP_FUTURE_RESPONSE;
   }
 
-  PRTime notAfter;
+  Time notAfter(Time::uninitialized);
   static const uint8_t NEXT_UPDATE_TAG =
     der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0;
   if (input.Peek(NEXT_UPDATE_TAG)) {
-    PRTime nextUpdate;
+    Time nextUpdate(Time::uninitialized);
     rv = der::Nested(input, NEXT_UPDATE_TAG,
                     bind(der::GeneralizedTime, _1, ref(nextUpdate)));
     if (rv != Success) {
       return rv;
     }
 
     if (nextUpdate < thisUpdate) {
       return Result::ERROR_OCSP_MALFORMED_RESPONSE;
     }
-    if (nextUpdate - thisUpdate <= maxLifetime) {
+    notAfter = thisUpdate;
+    if (notAfter.AddSeconds(context.maxLifetimeInDays *
+                            Time::ONE_DAY_IN_SECONDS) != Success) {
+      // This could only happen if we're dealing with times beyond the year
+      // 10,000AD.
+      return Result::ERROR_OCSP_FUTURE_RESPONSE;
+    }
+    if (nextUpdate <= notAfter) {
       notAfter = nextUpdate;
-    } else {
-      notAfter = thisUpdate + maxLifetime;
     }
   } else {
     // NSS requires all OCSP responses without a nextUpdate to be recent.
     // Match that stricter behavior.
-    notAfter = thisUpdate + ONE_DAY;
+    notAfter = thisUpdate;
+    if (notAfter.AddSeconds(Time::ONE_DAY_IN_SECONDS) != Success) {
+      // This could only happen if we're dealing with times beyond the year
+      // 10,000AD.
+      return Result::ERROR_OCSP_FUTURE_RESPONSE;
+    }
   }
 
-  if (context.time < SLOP) { // prevent underflow
-    return Result::FATAL_ERROR_INVALID_ARGS;
+  Time timeMinusSlop(context.time);
+  rv = timeMinusSlop.SubtractSeconds(SLOP_SECONDS);
+  if (rv != Success) {
+    return rv;
   }
-
-  if (context.time - SLOP > notAfter) {
+  if (timeMinusSlop > notAfter) {
     context.expired = true;
   }
 
   rv = der::OptionalExtensions(input,
                                der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
                                ExtensionNotUnderstood);
   if (rv != Success) {
     return rv;
new file mode 100644
--- /dev/null
+++ b/security/pkix/lib/pkixtime.cpp
@@ -0,0 +1,62 @@
+/* -*- 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 2014 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 "pkix/Time.h"
+#include "pkixutil.h"
+#ifdef WIN32
+#include "windows.h"
+#else
+#include "sys/time.h"
+#endif
+
+namespace mozilla { namespace pkix {
+
+Time
+Now()
+{
+  uint64_t seconds;
+
+#ifdef WIN32
+  // "Contains a 64-bit value representing the number of 100-nanosecond
+  // intervals since January 1, 1601 (UTC)."
+  //   - http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
+  FILETIME ft;
+  GetSystemTimeAsFileTime(&ft);
+  uint64_t ft64 = (static_cast<uint64_t>(ft.dwHighDateTime) << 32) |
+                  ft.dwLowDateTime;
+  seconds = (DaysBeforeYear(1601) * Time::ONE_DAY_IN_SECONDS) +
+            ft64 / (1000u * 1000u * 1000u / 100u);
+#else
+  // "The gettimeofday() function shall obtain the current time, expressed as
+  // seconds and microseconds since the Epoch."
+  //   - http://pubs.opengroup.org/onlinepubs/009695399/functions/gettimeofday.html
+  timeval tv;
+  (void) gettimeofday(&tv, nullptr);
+  seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) + tv.tv_sec;
+#endif
+
+  return TimeFromElapsedSecondsAD(seconds);
+}
+
+} } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixutil.h
+++ b/security/pkix/lib/pkixutil.h
@@ -177,11 +177,22 @@ public:
 private:
   Input items[MAX_LENGTH]; // avoids any heap allocations
   size_t numItems;
 
   NonOwningDERArray(const NonOwningDERArray&) /* = delete*/;
   void operator=(const NonOwningDERArray&) /* = delete*/;
 };
 
+inline unsigned int
+DaysBeforeYear(unsigned int year)
+{
+  PR_ASSERT(year >= 1);
+  PR_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.
+}
+
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkixutil_h
--- a/security/pkix/moz.build
+++ b/security/pkix/moz.build
@@ -7,16 +7,17 @@
 SOURCES += [
     'lib/pkixbind.cpp',
     'lib/pkixbuild.cpp',
     'lib/pkixcert.cpp',
     'lib/pkixcheck.cpp',
     'lib/pkixder.cpp',
     'lib/pkixnss.cpp',
     'lib/pkixocsp.cpp',
+    'lib/pkixtime.cpp',
 ]
 
 LOCAL_INCLUDES += [
     'include',
 ]
 
 TEST_DIRS += [
     'test/gtest',
--- a/security/pkix/test/gtest/nssgtest.cpp
+++ b/security/pkix/test/gtest/nssgtest.cpp
@@ -80,26 +80,28 @@ Pred_SECFailure(const char* expectedExpr
 
 /*static*/ void
 NSSTest::SetUpTestCase()
 {
   if (NSS_NoDB_Init(nullptr) != SECSuccess) {
     PR_Abort();
   }
 
-  now = PR_Now();
-  oneDayBeforeNow = now - ONE_DAY;
-  oneDayAfterNow = now + ONE_DAY;
+  now = Now();
+  pr_now = PR_Now();
+  pr_oneDayBeforeNow = pr_now - ONE_DAY;
+  pr_oneDayAfterNow = pr_now + ONE_DAY;
 }
 
 NSSTest::NSSTest()
   : arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE))
 {
   if (!arena) {
     PR_Abort();
   }
 }
 
-/*static*/ PRTime NSSTest::now;
-/*static*/ PRTime NSSTest::oneDayBeforeNow;
-/*static*/ PRTime NSSTest::oneDayAfterNow;
+/*static*/ mozilla::pkix::Time NSSTest::now(Now());
+/*static*/ PRTime NSSTest::pr_now;
+/*static*/ PRTime NSSTest::pr_oneDayBeforeNow;
+/*static*/ PRTime NSSTest::pr_oneDayAfterNow;
 
 } } } // namespace mozilla::pkix::test
--- a/security/pkix/test/gtest/nssgtest.h
+++ b/security/pkix/test/gtest/nssgtest.h
@@ -98,16 +98,17 @@ class NSSTest : public ::testing::Test
 {
 public:
   static void SetUpTestCase();
 
 protected:
   NSSTest();
 
   ScopedPLArenaPool arena;
-  static PRTime now;
-  static PRTime oneDayBeforeNow;
-  static PRTime oneDayAfterNow;
+  static mozilla::pkix::Time now;
+  static PRTime pr_now;
+  static PRTime pr_oneDayBeforeNow;
+  static PRTime pr_oneDayAfterNow;
 };
 
 } } } // namespace mozilla::pkix::test
 
 #endif // mozilla_pkix__nssgtest_h
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp
+++ b/security/pkix/test/gtest/pkixbuild_tests.cpp
@@ -121,24 +121,24 @@ private:
       trustLevel = TrustLevel::TrustAnchor;
     } else {
       trustLevel = TrustLevel::InheritsTrust;
     }
     return Success;
   }
 
   virtual Result FindIssuer(Input encodedIssuerName,
-                            IssuerChecker& checker, PRTime time)
+                            IssuerChecker& checker, Time time)
   {
     SECItem encodedIssuerNameSECItem =
       UnsafeMapInputToSECItem(encodedIssuerName);
     ScopedCERTCertList
       candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
-                                            &encodedIssuerNameSECItem, time,
-                                            true));
+                                            &encodedIssuerNameSECItem, 0,
+                                            false));
     if (candidates) {
       for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
            !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
         bool keepGoing;
         Input derCert;
         Result rv = derCert.Init(n->cert->derCert.data, n->cert->derCert.len);
         EXPECT_EQ(Success, rv);
         if (rv != Success) {
@@ -153,17 +153,17 @@ private:
           break;
         }
       }
     }
 
     return Success;
   }
 
-  virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
+  virtual Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
                                  /*optional*/ const Input*,
                                  /*optional*/ const Input*)
   {
     return Success;
   }
 
   virtual Result IsChainValid(const DERArray&)
   {
--- a/security/pkix/test/gtest/pkixcert_extension_tests.cpp
+++ b/security/pkix/test/gtest/pkixcert_extension_tests.cpp
@@ -77,23 +77,23 @@ private:
                               Input candidateCert,
                               /*out*/ TrustLevel& trustLevel)
   {
     trustLevel = TrustLevel::TrustAnchor;
     return Success;
   }
 
   virtual Result FindIssuer(Input /*encodedIssuerName*/,
-                            IssuerChecker& /*checker*/, PRTime /*time*/)
+                            IssuerChecker& /*checker*/, Time /*time*/)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
+  virtual Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
                                  /*optional*/ const Input*,
                                  /*optional*/ const Input*)
   {
     return Success;
   }
 
   virtual Result IsChainValid(const DERArray&)
   {
--- a/security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
+++ b/security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
@@ -26,45 +26,45 @@
 #include "pkix/pkixtypes.h"
 #include "pkixtestutil.h"
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 namespace mozilla { namespace pkix {
 
-Result CheckValidity(const Input encodedValidity, PRTime time);
+Result CheckValidity(const Input encodedValidity, Time time);
 
 } } // namespace mozilla::pkix
 
-static const PRTime PAST_TIME(YMDHMS(1998, 12, 31, 12, 23, 56));
+static const Time PAST_TIME(YMDHMS(1998, 12, 31, 12, 23, 56));
 
 #define OLDER_GENERALIZEDTIME \
   0x18, 15,                               /* tag, length */ \
   '1', '9', '9', '9', '0', '1', '0', '1', /* 1999-01-01 */ \
   '0', '0', '0', '0', '0', '0', 'Z'       /* 00:00:00Z */
 
 #define OLDER_UTCTIME \
   0x17, 13,                               /* tag, length */ \
   '9', '9', '0', '1', '0', '1',           /* (19)99-01-01 */ \
   '0', '0', '0', '0', '0', '0', 'Z'       /* 00:00:00Z */
 
-static const PRTime NOW(YMDHMS(2016, 12, 31, 12, 23, 56));
+static const Time NOW(YMDHMS(2016, 12, 31, 12, 23, 56));
 
 #define NEWER_GENERALIZEDTIME \
   0x18, 15,                               /* tag, length */ \
   '2', '0', '2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \
   '0', '0', '0', '0', '0', '0', 'Z'       /* 00:00:00Z */
 
 #define NEWER_UTCTIME \
   0x17, 13,                               /* tag, length */ \
   '2', '1', '0', '1', '0', '1',           /* 2021-01-01 */ \
   '0', '0', '0', '0', '0', '0', 'Z'       /* 00:00:00Z */
 
-static const PRTime FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
+static const Time FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
 
 class pkixcheck_CheckValidity : public ::testing::Test { };
 
 TEST_F(pkixcheck_CheckValidity, BothEmptyNull)
 {
   static const uint8_t DER[] = {
     0x17/*UTCTime*/, 0/*length*/,
     0x17/*UTCTime*/, 0/*length*/,
--- a/security/pkix/test/gtest/pkixder_universal_types_tests.cpp
+++ b/security/pkix/test/gtest/pkixder_universal_types_tests.cpp
@@ -310,17 +310,17 @@ TEST_F(pkixder_universal_types_tests, En
 // e.g. TWO_CHARS(53) => '5', '3'
 #define TWO_CHARS(t) static_cast<uint8_t>('0' + ((t) / 10u)), \
                      static_cast<uint8_t>('0' + ((t) % 10u))
 
 // Calls TimeChoice on the UTCTime variant of the given generalized time.
 template <uint16_t LENGTH>
 Result
 TimeChoiceForEquivalentUTCTime(const uint8_t (&generalizedTimeDER)[LENGTH],
-                               /*out*/ PRTime& value)
+                               /*out*/ Time& value)
 {
   static_assert(LENGTH >= 4,
                 "TimeChoiceForEquivalentUTCTime input too small");
   uint8_t utcTimeDER[LENGTH - 2];
   utcTimeDER[0] = 0x17; // tag UTCTime
   utcTimeDER[1] = LENGTH - 1/*tag*/ - 1/*value*/ - 2/*century*/;
   // Copy the value except for the first two digits of the year
   for (size_t i = 2; i < LENGTH - 2; ++i) {
@@ -329,69 +329,69 @@ TimeChoiceForEquivalentUTCTime(const uin
 
   Input input(utcTimeDER);
   Reader reader(input);
   return TimeChoice(reader, value);
 }
 
 template <uint16_t LENGTH>
 void
-ExpectGoodTime(PRTime expectedValue,
+ExpectGoodTime(Time expectedValue,
                const uint8_t (&generalizedTimeDER)[LENGTH])
 {
   // GeneralizedTime
   {
     Input input(generalizedTimeDER);
     Reader reader(input);
-    PRTime value = 0;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Success, GeneralizedTime(reader, value));
     EXPECT_EQ(expectedValue, value);
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input(generalizedTimeDER);
     Reader reader(input);
-    PRTime value = 0;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Success, TimeChoice(reader, value));
     EXPECT_EQ(expectedValue, value);
   }
 
   // TimeChoice: UTCTime
   {
-    PRTime value = 0;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Success,
               TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value));
     EXPECT_EQ(expectedValue, value);
   }
 }
 
 template <uint16_t LENGTH>
 void
 ExpectBadTime(const uint8_t (&generalizedTimeDER)[LENGTH])
 {
   // GeneralizedTime
   {
     Input input(generalizedTimeDER);
     Reader reader(input);
-    PRTime value;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(reader, value));
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input(generalizedTimeDER);
     Reader reader(input);
-    PRTime value;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(reader, value));
   }
 
   // TimeChoice: UTCTime
   {
-    PRTime value;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Result::ERROR_INVALID_TIME,
               TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value));
   }
 }
 
 // Control value: a valid time
 TEST_F(pkixder_universal_types_tests, ValidControl)
 {
@@ -416,17 +416,17 @@ TEST_F(pkixder_universal_types_tests, Ti
 
 TEST_F(pkixder_universal_types_tests, TimeInvalidZeroLength)
 {
   const uint8_t DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH[] = {
     0x18,                           // GeneralizedTime
     0x00                            // Length = 0
   };
 
-  PRTime value;
+  Time value(Time::uninitialized);
 
   // GeneralizedTime
   Input gtBuf(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH);
   Reader gt(gtBuf);
   ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(gt, value));
 
   // TimeChoice: GeneralizedTime
   Input tc_gt_buf(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH);
@@ -507,43 +507,43 @@ TEST_F(pkixder_universal_types_tests, Ge
     const uint8_t DER[] = {
       0x18,                           // Generalized Time
       15,                             // Length = 15
       TWO_CHARS(i / 100), TWO_CHARS(i % 100), // YYYY
       '1', '2', '3', '1', // 12-31
       '2', '3', '5', '9', '5', '9', 'Z' // 23:59:59Z
     };
 
-    PRTime expectedValue = YMDHMS(i, 12, 31, 23, 59, 59);
+    Time expectedValue = YMDHMS(i, 12, 31, 23, 59, 59);
 
     // We have to test GeneralizedTime separately from UTCTime instead of using
     // ExpectGooDtime because the range of UTCTime is less than the range of
     // GeneralizedTime.
 
     // GeneralizedTime
     {
       Input input(DER);
       Reader reader(input);
-      PRTime value = 0;
+      Time value(Time::uninitialized);
       ASSERT_EQ(Success, GeneralizedTime(reader, value));
       EXPECT_EQ(expectedValue, value);
     }
 
     // TimeChoice: GeneralizedTime
     {
       Input input(DER);
       Reader reader(input);
-      PRTime value = 0;
+      Time value(Time::uninitialized);
       ASSERT_EQ(Success, TimeChoice(reader, value));
       EXPECT_EQ(expectedValue, value);
     }
 
     // TimeChoice: UTCTime, which is limited to years less than 2049.
     if (i <= 2049) {
-      PRTime value = 0;
+      Time value(Time::uninitialized);
       ASSERT_EQ(Success, TimeChoiceForEquivalentUTCTime(DER, value));
       EXPECT_EQ(expectedValue, value);
     }
   }
 }
 
 // In order to ensure we we don't run into any trouble with conversions to and
 // from time_t we only accept times from 1970 onwards.
@@ -666,32 +666,32 @@ TEST_F(pkixder_universal_types_tests, Ti
     0x18,                           // Generalized Time
     15,                             // Length = 15
     '2', '4', '0', '0', '0', '2', '2', '9', // 2400-02-29
     '1', '6', '4', '5', '4', '0', 'Z' // 16:45:40
   };
 
   // We don't use ExpectGoodTime here because UTCTime can't represent 2400.
 
-  PRTime expectedValue = YMDHMS(2400, 2, 29, 16, 45, 40);
+  Time expectedValue = YMDHMS(2400, 2, 29, 16, 45, 40);
 
   // GeneralizedTime
   {
     Input input(DER);
     Reader reader(input);
-    PRTime value = 0;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Success, GeneralizedTime(reader, value));
     EXPECT_EQ(expectedValue, value);
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input(DER);
     Reader reader(input);
-    PRTime value = 0;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Success, TimeChoice(reader, value));
     EXPECT_EQ(expectedValue, value);
   }
 }
 
 TEST_F(pkixder_universal_types_tests, TimeMonthFebNotLeapYear2014)
 {
   static const uint8_t DER[] = {
@@ -713,25 +713,25 @@ TEST_F(pkixder_universal_types_tests, Ti
   };
 
   // We don't use ExpectBadTime here because UTCTime can't represent 2100.
 
   // GeneralizedTime
   {
     Input input(DER);
     Reader reader(input);
-    PRTime value;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(reader, value));
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input(DER);
     Reader reader(input);
-    PRTime value;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(reader, value));
   }
 }
 
 TEST_F(pkixder_universal_types_tests, TimeHoursValidRange)
 {
   for (uint8_t i = 0; i <= 23; ++i) {
     const uint8_t DER[] = {
@@ -851,25 +851,25 @@ TEST_F(pkixder_universal_types_tests, Ti
   // We can't use ExpectBadTime here, because ExpectBadTime requires
   // consistent results for GeneralizedTime and UTCTime, but the results
   // for this input are different.
 
   // GeneralizedTime
   {
     Input input(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR);
     Reader reader(input);
-    PRTime value = 0;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Result::ERROR_INVALID_TIME, GeneralizedTime(reader, value));
   }
 
   // TimeChoice: GeneralizedTime
   {
     Input input(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR);
     Reader reader(input);
-    PRTime value = 0;
+    Time value(Time::uninitialized);
     ASSERT_EQ(Result::ERROR_INVALID_TIME, TimeChoice(reader, value));
   }
 
   // This test is not applicable to TimeChoice: UTCTime
 }
 
 TEST_F(pkixder_universal_types_tests, TimeInvalidYearChar)
 {
--- a/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
+++ b/security/pkix/test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp
@@ -37,23 +37,23 @@ class CreateEncodedOCSPRequestTrustDomai
 private:
   virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
                               Input, /*out*/ TrustLevel&)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual Result FindIssuer(Input, IssuerChecker&, PRTime)
+  virtual Result FindIssuer(Input, IssuerChecker&, Time)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
-  virtual Result CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
+  virtual Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
                                  const Input*, const Input*)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result IsChainValid(const DERArray&)
   {
--- a/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
+++ b/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
@@ -46,24 +46,24 @@ public:
   Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&,
                       Input, /*out*/ TrustLevel& trustLevel)
   {
     EXPECT_EQ(endEntityOrCA, EndEntityOrCA::MustBeEndEntity);
     trustLevel = TrustLevel::InheritsTrust;
     return Success;
   }
 
-  Result FindIssuer(Input, IssuerChecker&, PRTime)
+  Result FindIssuer(Input, IssuerChecker&, Time)
   {
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
   virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID&,
-                                 PRTime time, /*optional*/ const Input*,
+                                 Time time, /*optional*/ const Input*,
                                  /*optional*/ const Input*)
   {
     // TODO: I guess mozilla::pkix should support revocation of designated
     // OCSP responder eventually, but we don't now, so this function should
     // never get called.
     ADD_FAILURE();
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
@@ -199,17 +199,17 @@ class pkixocsp_VerifyEncodedResponse_Wit
 {
 protected:
   // The result is owned by the arena
   Input CreateEncodedOCSPErrorResponse(uint8_t status)
   {
     static const Input EMPTY;
     OCSPResponseContext context(arena.get(),
                                 CertID(EMPTY, EMPTY, EMPTY),
-                                oneDayBeforeNow);
+                                pr_oneDayBeforeNow);
     context.responseStatus = status;
     context.skipResponseBytes = true;
     SECItem* response = CreateEncodedOCSPResponse(context);
     EXPECT_TRUE(response);
     // The result will be an empty Input on failure, but it doesn't
     // matter because the test is going to fail anyway.
     Input result;
     EXPECT_EQ(Success, result.Init(response->data, response->len));
@@ -291,74 +291,74 @@ public:
     return result;
   }
 };
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey)
 {
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID, byKey,
-                         rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                         &oneDayAfterNow));
+                         rootPrivateKey, pr_oneDayBeforeNow,
+                         pr_oneDayBeforeNow, &pr_oneDayAfterNow));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID,
                                       now, END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byName)
 {
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID, rootName,
-                         rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                         &oneDayAfterNow));
+                         rootPrivateKey, pr_oneDayBeforeNow,
+                         pr_oneDayBeforeNow, &pr_oneDayAfterNow));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey_without_nextUpdate)
 {
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID, byKey,
-                         rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                         nullptr));
+                         rootPrivateKey, pr_oneDayBeforeNow,
+                         pr_oneDayBeforeNow, nullptr));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, revoked)
 {
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::revoked, *endEntityCertID, byKey,
-                         rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                         &oneDayAfterNow));
+                         rootPrivateKey, pr_oneDayBeforeNow,
+                         pr_oneDayBeforeNow, &pr_oneDayAfterNow));
   bool expired;
   ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, unknown)
 {
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::unknown, *endEntityCertID, byKey,
-                         rootPrivateKey, oneDayBeforeNow, oneDayBeforeNow,
-                         &oneDayAfterNow));
+                         rootPrivateKey, pr_oneDayBeforeNow,
+                         pr_oneDayBeforeNow, &pr_oneDayAfterNow));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
@@ -399,35 +399,37 @@ protected:
         ? CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
                                     ExtensionCriticality::NotCritical)
         : nullptr,
       nullptr
     };
     ScopedSECKEYPrivateKey signerPrivateKey;
     SECItem* signerDER(CreateEncodedCertificate(
                           arena.get(), ++rootIssuedCount, rootName,
-                          oneDayBeforeNow, oneDayAfterNow, certSubjectName,
+                          pr_oneDayBeforeNow, pr_oneDayAfterNow,
+                          certSubjectName,
                           signerEKU != SEC_OID_UNKNOWN ? extensions : nullptr,
                           rootPrivateKey.get(), signerPrivateKey));
     EXPECT_TRUE(signerDER);
     if (signerDEROut) {
       EXPECT_EQ(Success,
                 signerDEROut->Init(signerDER->data, signerDER->len));
     }
 
     const SECItem* signerNameDER = nullptr;
     if (signerName) {
       signerNameDER = ASCIIToDERName(arena.get(), signerName);
       EXPECT_TRUE(signerNameDER);
     }
     SECItem const* const certs[] = { signerDER, nullptr };
     return CreateEncodedOCSPSuccessfulResponse(certStatus, *endEntityCertID,
                                                signerName, signerPrivateKey,
-                                               oneDayBeforeNow, oneDayBeforeNow,
-                                               &oneDayAfterNow, certs);
+                                               pr_oneDayBeforeNow,
+                                               pr_oneDayBeforeNow,
+                                               &pr_oneDayAfterNow, certs);
   }
 
   static SECItem* CreateEncodedCertificate(PLArenaPool* arena,
                                            uint32_t serialNumber,
                                            const char* issuer,
                                            PRTime notBefore,
                                            PRTime notAfter,
                                            const char* subject,
@@ -487,18 +489,18 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
        good_byKey_missing_signer)
 {
   ScopedSECKEYPublicKey missingSignerPublicKey;
   ScopedSECKEYPrivateKey missingSignerPrivateKey;
   ASSERT_SECSuccess(GenerateKeyPair(missingSignerPublicKey,
                                     missingSignerPrivateKey));
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID, byKey,
-                         missingSignerPrivateKey, oneDayBeforeNow,
-                         oneDayBeforeNow, nullptr));
+                         missingSignerPrivateKey, pr_oneDayBeforeNow,
+                         pr_oneDayBeforeNow, nullptr));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
@@ -507,17 +509,17 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
 {
   ScopedSECKEYPublicKey missingSignerPublicKey;
   ScopedSECKEYPrivateKey missingSignerPrivateKey;
   ASSERT_SECSuccess(GenerateKeyPair(missingSignerPublicKey,
                                     missingSignerPrivateKey));
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
                          "CN=missing", missingSignerPrivateKey,
-                         oneDayBeforeNow, oneDayBeforeNow, nullptr));
+                         pr_oneDayBeforeNow, pr_oneDayBeforeNow, nullptr));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
@@ -529,28 +531,28 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   const SECItem* extensions[] = {
     CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
                               ExtensionCriticality::NotCritical),
     nullptr
   };
   ScopedSECKEYPrivateKey signerPrivateKey;
   SECItem* signerDER(CreateEncodedCertificate(arena.get(), ++rootIssuedCount,
                                               rootName,
-                                              now - (10 * ONE_DAY),
-                                              now - (2 * ONE_DAY),
+                                              pr_now - (10 * ONE_DAY),
+                                              pr_now - (2 * ONE_DAY),
                                               signerName, extensions,
                                               rootPrivateKey.get(),
                                               signerPrivateKey));
   ASSERT_TRUE(signerDER);
 
   SECItem const* const certs[] = { signerDER, nullptr };
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
-                         signerName, signerPrivateKey, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow, certs));
+                         signerName, signerPrivateKey, pr_oneDayBeforeNow,
+                         pr_oneDayBeforeNow, &pr_oneDayAfterNow, certs));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_future)
@@ -561,28 +563,28 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   const SECItem* extensions[] = {
     CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
                               ExtensionCriticality::NotCritical),
     nullptr
   };
   ScopedSECKEYPrivateKey signerPrivateKey;
   SECItem* signerDER(CreateEncodedCertificate(arena.get(), ++rootIssuedCount,
                                               rootName,
-                                              now + (2 * ONE_DAY),
-                                              now + (10 * ONE_DAY),
+                                              pr_now + (2 * ONE_DAY),
+                                              pr_now + (10 * ONE_DAY),
                                               signerName, extensions,
                                               rootPrivateKey.get(),
                                               signerPrivateKey));
   ASSERT_TRUE(signerDER);
 
   SECItem const* const certs[] = { signerDER, nullptr };
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
-                         signerName, signerPrivateKey, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow, certs));
+                         signerName, signerPrivateKey, pr_oneDayBeforeNow,
+                         pr_oneDayBeforeNow, &pr_oneDayAfterNow, certs));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
@@ -656,27 +658,27 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER;
   const SECItem* extensions[] = {
     CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
                               ExtensionCriticality::NotCritical),
     nullptr
   };
   ScopedSECKEYPrivateKey signerPrivateKey;
   SECItem* signerDER(CreateEncodedCertificate(arena.get(), 1,
-                        subCAName, oneDayBeforeNow, oneDayAfterNow,
+                        subCAName, pr_oneDayBeforeNow, pr_oneDayAfterNow,
                         signerName, extensions, unknownPrivateKey.get(),
                         signerPrivateKey));
   ASSERT_TRUE(signerDER);
 
   // OCSP response signed by that delegated responder
   SECItem const* const certs[] = { signerDER, nullptr };
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
-                         signerName, signerPrivateKey, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow, certs));
+                         signerName, signerPrivateKey, pr_oneDayBeforeNow,
+                         pr_oneDayBeforeNow, &pr_oneDayAfterNow, certs));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
@@ -693,44 +695,45 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   const SECItem* subCAExtensions[] = {
     CreateEncodedBasicConstraints(arena.get(), true, 0,
                                   ExtensionCriticality::NotCritical),
     nullptr
   };
   ScopedSECKEYPrivateKey subCAPrivateKey;
   SECItem* subCADER(CreateEncodedCertificate(arena.get(), ++rootIssuedCount,
                                              rootName,
-                                             oneDayBeforeNow, oneDayAfterNow,
+                                             pr_oneDayBeforeNow, pr_oneDayAfterNow,
                                              subCAName, subCAExtensions,
                                              rootPrivateKey.get(),
                                              subCAPrivateKey));
   ASSERT_TRUE(subCADER);
 
   // Delegated responder cert signed by that sub-CA
   static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER;
   const SECItem* extensions[] = {
     CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
                               ExtensionCriticality::NotCritical),
     nullptr
   };
   ScopedSECKEYPrivateKey signerPrivateKey;
   SECItem* signerDER(CreateEncodedCertificate(arena.get(), 1, subCAName,
-                                              oneDayBeforeNow, oneDayAfterNow,
+                                              pr_oneDayBeforeNow,
+                                              pr_oneDayAfterNow,
                                               signerName, extensions,
                                               subCAPrivateKey.get(),
                                               signerPrivateKey));
   ASSERT_TRUE(signerDER);
 
   // OCSP response signed by the delegated responder issued by the sub-CA
   // that is trying to impersonate the root.
   SECItem const* const certs[] = { subCADER, signerDER, nullptr };
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
-                         signerName, signerPrivateKey, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow, certs));
+                         signerName, signerPrivateKey, pr_oneDayBeforeNow,
+                         pr_oneDayBeforeNow, &pr_oneDayAfterNow, certs));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
@@ -747,44 +750,46 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   const SECItem* subCAExtensions[] = {
     CreateEncodedBasicConstraints(arena.get(), true, 0,
                                   ExtensionCriticality::NotCritical),
     nullptr
   };
   ScopedSECKEYPrivateKey subCAPrivateKey;
   SECItem* subCADER(CreateEncodedCertificate(arena.get(), ++rootIssuedCount,
                                              rootName,
-                                             oneDayBeforeNow, oneDayAfterNow,
+                                             pr_oneDayBeforeNow,
+                                             pr_oneDayAfterNow,
                                              subCAName, subCAExtensions,
                                              rootPrivateKey.get(),
                                              subCAPrivateKey));
   ASSERT_TRUE(subCADER);
 
   // Delegated responder cert signed by that sub-CA
   static const SECOidTag signerEKU = SEC_OID_OCSP_RESPONDER;
   const SECItem* extensions[] = {
     CreateEncodedEKUExtension(arena.get(), &signerEKU, 1,
                               ExtensionCriticality::NotCritical),
     nullptr
   };
   ScopedSECKEYPrivateKey signerPrivateKey;
   SECItem* signerDER(CreateEncodedCertificate(arena.get(), 1, subCAName,
-                                              oneDayBeforeNow, oneDayAfterNow,
+                                              pr_oneDayBeforeNow,
+                                              pr_oneDayAfterNow,
                                               signerName, extensions,
                                               subCAPrivateKey.get(),
                                               signerPrivateKey));
   ASSERT_TRUE(signerDER);
 
   // OCSP response signed by the delegated responder issued by the sub-CA
   // that is trying to impersonate the root.
   SECItem const* const certs[] = { signerDER, subCADER, nullptr };
   Input response(CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
-                         signerName, signerPrivateKey, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow, certs));
+                         signerName, signerPrivateKey, pr_oneDayBeforeNow,
+                         pr_oneDayBeforeNow, &pr_oneDayAfterNow, certs));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, now,
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
--- a/security/pkix/test/lib/pkixtestutil.cpp
+++ b/security/pkix/test/lib/pkixtestutil.cpp
@@ -28,16 +28,17 @@
 #include <limits>
 #include <new>
 
 #include "cryptohi.h"
 #include "hasht.h"
 #include "pk11pub.h"
 #include "pkix/pkixnss.h"
 #include "pkixder.h"
+#include "pkixutil.h"
 #include "prerror.h"
 #include "prinit.h"
 #include "prprf.h"
 #include "secder.h"
 #include "secerr.h"
 
 using namespace std;
 
@@ -487,31 +488,34 @@ PRTimeToTimeChoice(PLArenaPool* arena, P
 {
   PRExplodedTime exploded;
   PR_ExplodeTime(time, PR_GMTParameters, &exploded);
   return PRTimeToEncodedTime(arena, time,
     (exploded.tm_year >= 1950 && exploded.tm_year < 2050) ? UTCTime
                                                           : GeneralizedTime);
 }
 
-PRTime
+Time
 YMDHMS(int16_t year, int16_t month, int16_t day,
        int16_t hour, int16_t minutes, int16_t seconds)
 {
   PRExplodedTime tm;
   tm.tm_usec = 0;
   tm.tm_sec = seconds;
   tm.tm_min = minutes;
   tm.tm_hour = hour;
   tm.tm_mday = day;
   tm.tm_month = month - 1; // tm_month is zero-based
   tm.tm_year = year;
   tm.tm_params.tp_gmt_offset = 0;
   tm.tm_params.tp_dst_offset = 0;
-  return PR_ImplodeTime(&tm);
+  PRTime time = PR_ImplodeTime(&tm);
+  return TimeFromElapsedSecondsAD((time / PR_USEC_PER_SEC) +
+                                  (DaysBeforeYear(1970) *
+                                   Time::ONE_DAY_IN_SECONDS));
 }
 
 static SECItem*
 SignedData(PLArenaPool* arena, const SECItem* tbsData,
            SECKEYPrivateKey* privKey, SECOidTag hashAlg,
            bool corrupt, /*optional*/ SECItem const* const* certs)
 {
   PR_ASSERT(arena);
--- a/security/pkix/test/lib/pkixtestutil.h
+++ b/security/pkix/test/lib/pkixtestutil.h
@@ -71,18 +71,18 @@ typedef mozilla::pkix::ScopedPtr<SECKEYP
 typedef mozilla::pkix::ScopedPtr<SECKEYPrivateKey, SECKEY_DestroyPrivateKey>
   ScopedSECKEYPrivateKey;
 
 FILE* OpenFile(const char* dir, const char* filename, const char* mode);
 
 extern const PRTime ONE_DAY;
 
 // e.g. YMDHMS(2016, 12, 31, 1, 23, 45) => 2016-12-31:01:23:45 (GMT)
-PRTime YMDHMS(int16_t year, int16_t month, int16_t day,
-              int16_t hour, int16_t minutes, int16_t seconds);
+mozilla::pkix::Time YMDHMS(int16_t year, int16_t month, int16_t day,
+                           int16_t hour, int16_t minutes, int16_t seconds);
 
 SECStatus GenerateKeyPair(/*out*/ ScopedSECKEYPublicKey& publicKey,
                           /*out*/ ScopedSECKEYPrivateKey& privateKey);
 
 // The result will be owned by the arena
 const SECItem* ASCIIToDERName(PLArenaPool* arena, const char* cn);
 
 // Replace one substring in item with another of the same length, but only if