Bug 1026261: Remove CERTCertificate from mozilla::pkix revocation checking API, r=keeler

--HG--
extra : rebase_source : 6798f494bd351961ea02abba07b5860839bbc418
This commit is contained in:
Brian Smith 2014-06-20 10:10:51 -07:00
parent c751f855a6
commit ca4f473450
19 changed files with 476 additions and 455 deletions

View File

@ -170,10 +170,8 @@ AppTrustDomain::VerifySignedData(const CERTSignedData* signedData,
}
SECStatus
AppTrustDomain::CheckRevocation(EndEntityOrCA,
const CERTCertificate*,
/*const*/ CERTCertificate*,
PRTime time,
AppTrustDomain::CheckRevocation(EndEntityOrCA, const CertID&, PRTime time,
/*optional*/ const SECItem*,
/*optional*/ const SECItem*)
{
// We don't currently do revocation checking. If we need to distrust an Apps

View File

@ -31,10 +31,9 @@ public:
SECStatus VerifySignedData(const CERTSignedData* signedData,
const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE;
SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
const CERTCertificate* cert,
/*const*/ CERTCertificate* issuerCertToDup,
PRTime time,
/*optional*/ const SECItem* stapledOCSPresponse);
const mozilla::pkix::CertID& certID, PRTime time,
/*optional*/ const SECItem* stapledOCSPresponse,
/*optional*/ const SECItem* aiaExtension);
SECStatus IsChainValid(const CERTCertList* certChain) { return SECSuccess; }
private:

View File

@ -175,13 +175,67 @@ OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching)
return PR_SecondsToInterval(2);
}
// Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
// CERT_GetGeneralNameByType. Returns SECFailure on error, SECSuccess
// with url == nullptr when an OCSP URI was not found, and SECSuccess with
// url != nullptr when an OCSP URI was found. The output url will be owned
// by the arena.
static SECStatus
GetOCSPAuthorityInfoAccessLocation(PLArenaPool* arena,
const SECItem& aiaExtension,
/*out*/ char const*& url)
{
url = nullptr;
// TODO(bug 1028380): Remove the const_cast.
CERTAuthInfoAccess** aia = CERT_DecodeAuthInfoAccessExtension(
arena,
const_cast<SECItem*>(&aiaExtension));
if (!aia) {
PR_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION, 0);
return SECFailure;
}
for (size_t i = 0; aia[i]; ++i) {
if (SECOID_FindOIDTag(&aia[i]->method) == SEC_OID_PKIX_OCSP) {
// NSS chooses the **last** OCSP URL; we choose the **first**
CERTGeneralName* current = aia[i]->location;
if (!current) {
continue;
}
do {
if (current->type == certURI) {
const SECItem& location = current->name.other;
// (location.len + 1) must be small enough to fit into a uint32_t,
// but we limit it to a smaller bound to reduce OOM risk.
if (location.len > 1024 || memchr(location.data, 0, location.len)) {
// Reject embedded nulls. (NSS doesn't do this)
PR_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION, 0);
return SECFailure;
}
// Copy the non-null-terminated SECItem into a null-terminated string.
char* nullTerminatedURL(static_cast<char*>(
PORT_ArenaAlloc(arena, location.len + 1)));
if (!nullTerminatedURL) {
return SECFailure;
}
memcpy(nullTerminatedURL, location.data, location.len);
nullTerminatedURL[location.len] = 0;
url = nullTerminatedURL;
return SECSuccess;
}
current = CERT_GetNextGeneralName(current);
} while (current != aia[i]->location);
}
}
return SECSuccess;
}
SECStatus
NSSCertDBTrustDomain::CheckRevocation(
mozilla::pkix::EndEntityOrCA endEntityOrCA,
const CERTCertificate* cert,
/*const*/ CERTCertificate* issuerCert,
PRTime time,
/*optional*/ const SECItem* stapledOCSPResponse)
NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
const CertID& certID, PRTime time,
/*optional*/ const SECItem* stapledOCSPResponse,
/*optional*/ const SECItem* aiaExtension)
{
// Actively distrusted certificates will have already been blocked by
// GetCertTrust.
@ -192,13 +246,6 @@ NSSCertDBTrustDomain::CheckRevocation(
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
("NSSCertDBTrustDomain: Top of CheckRevocation\n"));
PORT_Assert(cert);
PORT_Assert(issuerCert);
if (!cert || !issuerCert) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
// Bug 991815: The BR allow OCSP for intermediates to be up to one year old.
// Since this affects EV there is no reason why DV should be more strict
// so all intermediatates are allowed to have OCSP responses up to one year
@ -218,10 +265,9 @@ NSSCertDBTrustDomain::CheckRevocation(
if (stapledOCSPResponse) {
PR_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
bool expired;
SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert,
time,
SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
maxOCSPLifetimeInDays,
stapledOCSPResponse,
*stapledOCSPResponse,
ResponseWasStapled,
expired);
if (rv == SECSuccess) {
@ -254,7 +300,7 @@ NSSCertDBTrustDomain::CheckRevocation(
PRErrorCode cachedResponseErrorCode = 0;
PRTime cachedResponseValidThrough = 0;
bool cachedResponsePresent = mOCSPCache.Get(cert, issuerCert,
bool cachedResponsePresent = mOCSPCache.Get(certID,
cachedResponseErrorCode,
cachedResponseValidThrough);
if (cachedResponsePresent) {
@ -332,8 +378,19 @@ NSSCertDBTrustDomain::CheckRevocation(
return SECFailure;
}
ScopedPtr<char, PORT_Free_string>
url(CERT_GetOCSPAuthorityInfoAccessLocation(cert));
ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
if (!arena) {
return SECFailure;
}
const char* url = nullptr; // owned by the arena
if (aiaExtension) {
if (GetOCSPAuthorityInfoAccessLocation(arena.get(), *aiaExtension, url)
!= SECSuccess) {
return SECFailure;
}
}
if (!url) {
if (mOCSPFetching == FetchOCSPForEV ||
@ -357,24 +414,18 @@ NSSCertDBTrustDomain::CheckRevocation(
return SECSuccess;
}
ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
if (!arena) {
return SECFailure;
}
// Only request a response if we didn't have a cached indication of failure
// (don't keep requesting responses from a failing server).
const SECItem* response = nullptr;
if (cachedResponseErrorCode == 0 ||
cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT ||
cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) {
const SECItem* request(CreateEncodedOCSPRequest(arena.get(), cert,
issuerCert));
const SECItem* request(CreateEncodedOCSPRequest(arena.get(), certID));
if (!request) {
return SECFailure;
}
response = DoOCSPRequest(arena.get(), url.get(), request,
response = DoOCSPRequest(arena.get(), url, request,
OCSPFetchingTypeToTimeoutTime(mOCSPFetching),
mOCSPGetConfig == CertVerifier::ocsp_get_enabled);
}
@ -385,7 +436,7 @@ NSSCertDBTrustDomain::CheckRevocation(
error = cachedResponseErrorCode;
}
PRTime timeout = time + ServerFailureDelay;
if (mOCSPCache.Put(cert, issuerCert, error, time, timeout) != SECSuccess) {
if (mOCSPCache.Put(certID, error, time, timeout) != SECSuccess) {
return SECFailure;
}
PR_SetError(error, 0);
@ -420,9 +471,9 @@ NSSCertDBTrustDomain::CheckRevocation(
// or unknown certificate, PR_GetError() will return the appropriate error.
// We actually ignore expired here.
bool expired;
SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert, time,
SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
maxOCSPLifetimeInDays,
response,
*response,
ResponseIsFromNetwork,
expired);
if (rv == SECSuccess || mOCSPFetching != FetchOCSPForDVSoftFail) {
@ -453,13 +504,13 @@ NSSCertDBTrustDomain::CheckRevocation(
SECStatus
NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
const CERTCertificate* cert, CERTCertificate* issuerCert, PRTime time,
uint16_t maxLifetimeInDays, const SECItem* encodedResponse,
EncodedResponseSource responseSource, /*out*/ bool& expired)
const CertID& certID, PRTime time, uint16_t maxLifetimeInDays,
const SECItem& encodedResponse, EncodedResponseSource responseSource,
/*out*/ bool& expired)
{
PRTime thisUpdate = 0;
PRTime validThrough = 0;
SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time,
SECStatus rv = VerifyEncodedOCSPResponse(*this, certID, time,
maxLifetimeInDays, encodedResponse,
expired, &thisUpdate, &validThrough);
PRErrorCode error = (rv == SECSuccess ? 0 : PR_GetError());
@ -483,8 +534,7 @@ NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
error == SEC_ERROR_OCSP_UNKNOWN_CERT) {
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
("NSSCertDBTrustDomain: caching OCSP response"));
if (mOCSPCache.Put(cert, issuerCert, error, thisUpdate, validThrough)
!= SECSuccess) {
if (mOCSPCache.Put(certID, error, thisUpdate, validThrough) != SECSuccess) {
return SECFailure;
}
}

View File

@ -75,10 +75,10 @@ public:
const SECItem& subjectPublicKeyInfo);
virtual SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
const CERTCertificate* cert,
/*const*/ CERTCertificate* issuerCert,
const mozilla::pkix::CertID& certID,
PRTime time,
/*optional*/ const SECItem* stapledOCSPResponse);
/*optional*/ const SECItem* stapledOCSPResponse,
/*optional*/ const SECItem* aiaExtension);
virtual SECStatus IsChainValid(const CERTCertList* certChain);
@ -89,8 +89,8 @@ private:
};
static const PRTime ServerFailureDelay = 5 * 60 * PR_USEC_PER_SEC;
SECStatus VerifyAndMaybeCacheEncodedOCSPResponse(
const CERTCertificate* cert, CERTCertificate* issuerCert, PRTime time,
uint16_t maxLifetimeInDays, const SECItem* encodedResponse,
const mozilla::pkix::CertID& certID, PRTime time,
uint16_t maxLifetimeInDays, const SECItem& encodedResponse,
EncodedResponseSource responseSource, /*out*/ bool& expired);
const SECTrustType mCertDBTrustType;

View File

@ -28,12 +28,15 @@
#include "NSSCertDBTrustDomain.h"
#include "pk11pub.h"
#include "pkix/pkixtypes.h"
#include "secerr.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gCertVerifierLog;
#endif
using namespace mozilla::pkix;
namespace mozilla { namespace psm {
void
@ -58,8 +61,7 @@ typedef mozilla::pkix::ScopedPtr<PK11Context,
// is computationally infeasible to find collisions that would subvert this
// cache (given that SHA384 is a cryptographically-secure hash function).
static SECStatus
CertIDHash(SHA384Buffer& buf, const CERTCertificate* aCert,
const CERTCertificate* aIssuerCert)
CertIDHash(SHA384Buffer& buf, const CertID& certID)
{
ScopedPK11Context context(PK11_CreateDigestContext(SEC_OID_SHA384));
if (!context) {
@ -69,18 +71,17 @@ CertIDHash(SHA384Buffer& buf, const CERTCertificate* aCert,
if (rv != SECSuccess) {
return rv;
}
rv = PK11_DigestOp(context.get(), aCert->derIssuer.data,
aCert->derIssuer.len);
rv = PK11_DigestOp(context.get(), certID.issuer.data, certID.issuer.len);
if (rv != SECSuccess) {
return rv;
}
rv = PK11_DigestOp(context.get(), aIssuerCert->derPublicKey.data,
aIssuerCert->derPublicKey.len);
rv = PK11_DigestOp(context.get(), certID.issuerSubjectPublicKeyInfo.data,
certID.issuerSubjectPublicKeyInfo.len);
if (rv != SECSuccess) {
return rv;
}
rv = PK11_DigestOp(context.get(), aCert->serialNumber.data,
aCert->serialNumber.len);
rv = PK11_DigestOp(context.get(), certID.serialNumber.data,
certID.serialNumber.len);
if (rv != SECSuccess) {
return rv;
}
@ -93,16 +94,13 @@ CertIDHash(SHA384Buffer& buf, const CERTCertificate* aCert,
}
SECStatus
OCSPCache::Entry::Init(const CERTCertificate* aCert,
const CERTCertificate* aIssuerCert,
PRErrorCode aErrorCode,
PRTime aThisUpdate,
PRTime aValidThrough)
OCSPCache::Entry::Init(const CertID& aCertID, PRErrorCode aErrorCode,
PRTime aThisUpdate, PRTime aValidThrough)
{
mErrorCode = aErrorCode;
mThisUpdate = aThisUpdate;
mValidThrough = aValidThrough;
return CertIDHash(mIDHash, aCert, aIssuerCert);
return CertIDHash(mIDHash, aCertID);
}
OCSPCache::OCSPCache()
@ -118,9 +116,7 @@ OCSPCache::~OCSPCache()
// Returns false with index in an undefined state if no matching entry was
// found.
bool
OCSPCache::FindInternal(const CERTCertificate* aCert,
const CERTCertificate* aIssuerCert,
/*out*/ size_t& index,
OCSPCache::FindInternal(const CertID& aCertID, /*out*/ size_t& index,
const MutexAutoLock& /* aProofOfLock */)
{
if (mEntries.length() == 0) {
@ -128,7 +124,7 @@ OCSPCache::FindInternal(const CERTCertificate* aCert,
}
SHA384Buffer idHash;
SECStatus rv = CertIDHash(idHash, aCert, aIssuerCert);
SECStatus rv = CertIDHash(idHash, aCertID);
if (rv != SECSuccess) {
return false;
}
@ -145,19 +141,10 @@ OCSPCache::FindInternal(const CERTCertificate* aCert,
return false;
}
void
OCSPCache::LogWithCerts(const char* aMessage, const CERTCertificate* aCert,
const CERTCertificate* aIssuerCert)
static inline void
LogWithCertID(const char* aMessage, const CertID& aCertID)
{
#ifdef PR_LOGGING
if (PR_LOG_TEST(gCertVerifierLog, PR_LOG_DEBUG)) {
mozilla::pkix::ScopedPtr<char, mozilla::psm::PORT_Free_string>
cn(CERT_GetCommonName(&aCert->subject));
mozilla::pkix::ScopedPtr<char, mozilla::psm::PORT_Free_string>
cnIssuer(CERT_GetCommonName(&aIssuerCert->subject));
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, (aMessage, cn.get(), cnIssuer.get()));
}
#endif
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, (aMessage, &aCertID));
}
void
@ -172,22 +159,17 @@ OCSPCache::MakeMostRecentlyUsed(size_t aIndex,
}
bool
OCSPCache::Get(const CERTCertificate* aCert,
const CERTCertificate* aIssuerCert,
PRErrorCode& aErrorCode,
OCSPCache::Get(const CertID& aCertID, PRErrorCode& aErrorCode,
PRTime& aValidThrough)
{
PR_ASSERT(aCert);
PR_ASSERT(aIssuerCert);
MutexAutoLock lock(mMutex);
size_t index;
if (!FindInternal(aCert, aIssuerCert, index, lock)) {
LogWithCerts("OCSPCache::Get(%s, %s) not in cache", aCert, aIssuerCert);
if (!FindInternal(aCertID, index, lock)) {
LogWithCertID("OCSPCache::Get(%p) not in cache", aCertID);
return false;
}
LogWithCerts("OCSPCache::Get(%s, %s) in cache", aCert, aIssuerCert);
LogWithCertID("OCSPCache::Get(%p) in cache", aCertID);
aErrorCode = mEntries[index]->mErrorCode;
aValidThrough = mEntries[index]->mValidThrough;
MakeMostRecentlyUsed(index, lock);
@ -195,23 +177,17 @@ OCSPCache::Get(const CERTCertificate* aCert,
}
SECStatus
OCSPCache::Put(const CERTCertificate* aCert,
const CERTCertificate* aIssuerCert,
PRErrorCode aErrorCode,
PRTime aThisUpdate,
PRTime aValidThrough)
OCSPCache::Put(const CertID& aCertID, PRErrorCode aErrorCode,
PRTime aThisUpdate, PRTime aValidThrough)
{
PR_ASSERT(aCert);
PR_ASSERT(aIssuerCert);
MutexAutoLock lock(mMutex);
size_t index;
if (FindInternal(aCert, aIssuerCert, index, lock)) {
if (FindInternal(aCertID, index, lock)) {
// Never replace an entry indicating a revoked certificate.
if (mEntries[index]->mErrorCode == SEC_ERROR_REVOKED_CERTIFICATE) {
LogWithCerts("OCSPCache::Put(%s, %s) already in cache as revoked - "
"not replacing", aCert, aIssuerCert);
LogWithCertID("OCSPCache::Put(%p) already in cache as revoked - "
"not replacing", aCertID);
MakeMostRecentlyUsed(index, lock);
return SECSuccess;
}
@ -220,8 +196,8 @@ OCSPCache::Put(const CERTCertificate* aCert,
// indicates a revoked certificate, which we want to remember.
if (mEntries[index]->mThisUpdate > aThisUpdate &&
aErrorCode != SEC_ERROR_REVOKED_CERTIFICATE) {
LogWithCerts("OCSPCache::Put(%s, %s) already in cache with more recent "
"validity - not replacing", aCert, aIssuerCert);
LogWithCertID("OCSPCache::Put(%p) already in cache with more recent "
"validity - not replacing", aCertID);
MakeMostRecentlyUsed(index, lock);
return SECSuccess;
}
@ -230,14 +206,13 @@ OCSPCache::Put(const CERTCertificate* aCert,
// or revoked certificate should replace previously known responses.
if (aErrorCode != 0 && aErrorCode != SEC_ERROR_OCSP_UNKNOWN_CERT &&
aErrorCode != SEC_ERROR_REVOKED_CERTIFICATE) {
LogWithCerts("OCSPCache::Put(%s, %s) already in cache - not replacing "
"with less important status", aCert, aIssuerCert);
LogWithCertID("OCSPCache::Put(%p) already in cache - not replacing "
"with less important status", aCertID);
MakeMostRecentlyUsed(index, lock);
return SECSuccess;
}
LogWithCerts("OCSPCache::Put(%s, %s) already in cache - replacing",
aCert, aIssuerCert);
LogWithCertID("OCSPCache::Put(%p) already in cache - replacing", aCertID);
mEntries[index]->mErrorCode = aErrorCode;
mEntries[index]->mThisUpdate = aThisUpdate;
mEntries[index]->mValidThrough = aValidThrough;
@ -246,8 +221,7 @@ OCSPCache::Put(const CERTCertificate* aCert,
}
if (mEntries.length() == MaxEntries) {
LogWithCerts("OCSPCache::Put(%s, %s) too full - evicting an entry", aCert,
aIssuerCert);
LogWithCertID("OCSPCache::Put(%p) too full - evicting an entry", aCertID);
for (Entry** toEvict = mEntries.begin(); toEvict != mEntries.end();
toEvict++) {
// Never evict an entry that indicates a revoked or unknokwn certificate,
@ -283,14 +257,14 @@ OCSPCache::Put(const CERTCertificate* aCert,
PR_SetError(SEC_ERROR_NO_MEMORY, 0);
return SECFailure;
}
SECStatus rv = newEntry->Init(aCert, aIssuerCert, aErrorCode, aThisUpdate,
SECStatus rv = newEntry->Init(aCertID, aErrorCode, aThisUpdate,
aValidThrough);
if (rv != SECSuccess) {
delete newEntry;
return rv;
}
mEntries.append(newEntry);
LogWithCerts("OCSPCache::Put(%s, %s) added to cache", aCert, aIssuerCert);
LogWithCertID("OCSPCache::Put(%p) added to cache", aCertID);
return SECSuccess;
}

View File

@ -25,14 +25,17 @@
#ifndef mozilla_psm_OCSPCache_h
#define mozilla_psm_OCSPCache_h
#include "certt.h"
#include "hasht.h"
#include "pkix/pkixtypes.h"
#include "mozilla/Mutex.h"
#include "mozilla/Vector.h"
#include "prerror.h"
#include "prtime.h"
#include "seccomon.h"
namespace mozilla { namespace pkix {
struct CertID;
} } // namespace mozilla::pkix
namespace mozilla { namespace psm {
// make SHA384Buffer be of type "array of uint8_t of length SHA384_LENGTH"
@ -54,7 +57,7 @@ public:
// 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 CERTCertificate* aCert, const CERTCertificate* aIssuerCert,
bool Get(const mozilla::pkix::CertID& aCertID,
/* out */ PRErrorCode& aErrorCode, /* out */ PRTime& aValidThrough);
// Caches the status of the given certificate (issued by the given issuer).
@ -66,11 +69,8 @@ public:
// 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.
SECStatus Put(const CERTCertificate* aCert,
const CERTCertificate* aIssuerCert,
PRErrorCode aErrorCode,
PRTime aThisUpdate,
PRTime aValidThrough);
SECStatus Put(const mozilla::pkix::CertID& aCertID, PRErrorCode aErrorCode,
PRTime aThisUpdate, PRTime aValidThrough);
// Removes everything from the cache.
void Clear();
@ -79,10 +79,8 @@ private:
class Entry
{
public:
SECStatus Init(const CERTCertificate* aCert,
const CERTCertificate* aIssuerCert,
PRErrorCode aErrorCode, PRTime aThisUpdate,
PRTime aValidThrough);
SECStatus Init(const mozilla::pkix::CertID& aCertID, PRErrorCode aErrorCode,
PRTime aThisUpdate, PRTime aValidThrough);
PRErrorCode mErrorCode;
PRTime mThisUpdate;
@ -93,13 +91,9 @@ private:
SHA384Buffer mIDHash;
};
bool FindInternal(const CERTCertificate* aCert,
const CERTCertificate* aIssuerCert,
/*out*/ size_t& index,
bool FindInternal(const mozilla::pkix::CertID& aCertID, /*out*/ size_t& index,
const MutexAutoLock& aProofOfLock);
void MakeMostRecentlyUsed(size_t aIndex, const MutexAutoLock& aProofOfLock);
void LogWithCerts(const char* aMessage, const CERTCertificate* aCert,
const CERTCertificate* aIssuerCert);
Mutex mMutex;
static const size_t MaxEntries = 1024;

View File

@ -6,12 +6,15 @@
#include "CertVerifier.h"
#include "OCSPCache.h"
#include "gtest/gtest.h"
#include "nss.h"
#include "pkix/pkixtypes.h"
#include "prerr.h"
#include "prprf.h"
#include "secerr.h"
#include "gtest/gtest.h"
using namespace mozilla::pkix;
using namespace mozilla::psm;
const int MaxCacheEntries = 1024;
@ -27,179 +30,207 @@ class OCSPCacheTest : public ::testing::Test
mozilla::psm::OCSPCache cache;
};
// Makes a fake certificate with just the fields we need for testing here.
// (And those values are almost entirely bogus.)
// stackCert should be stack-allocated memory.
static void
MakeFakeCert(CERTCertificate* stackCert, const char* subjectValue,
const char* issuerValue, const char* serialNumberValue,
const char* publicKeyValue)
{
stackCert->derSubject.data = (unsigned char*)subjectValue;
stackCert->derSubject.len = strlen(subjectValue);
stackCert->derIssuer.data = (unsigned char*)issuerValue;
stackCert->derIssuer.len = strlen(issuerValue);
stackCert->serialNumber.data = (unsigned char*)serialNumberValue;
stackCert->serialNumber.len = strlen(serialNumberValue);
stackCert->derPublicKey.data = (unsigned char*)publicKeyValue;
stackCert->derPublicKey.len = strlen(publicKeyValue);
CERTName *subject = CERT_AsciiToName(subjectValue); // TODO: this will leak...
ASSERT_TRUE(subject);
stackCert->subject.arena = subject->arena;
stackCert->subject.rdns = subject->rdns;
}
static void
PutAndGet(mozilla::psm::OCSPCache& cache, CERTCertificate* subject,
CERTCertificate* issuer,
PRErrorCode error, PRTime time)
PutAndGet(OCSPCache& cache, const CertID& certID, PRErrorCode error,
PRTime 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);
SECStatus rv = cache.Put(subject, issuer, error, time - 10, time);
SECStatus rv = cache.Put(certID, error, time - 10, time);
ASSERT_TRUE(rv == SECSuccess);
PRErrorCode errorOut;
PRTime timeOut;
ASSERT_TRUE(cache.Get(subject, issuer, errorOut, timeOut));
ASSERT_TRUE(cache.Get(certID, errorOut, timeOut));
ASSERT_TRUE(error == errorOut && time == timeOut);
}
static const SECItem fakeIssuer1 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("CN=issuer1")),
10
};
static const SECItem fakeKey000 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("key000")),
6
};
static const SECItem fakeKey001 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("key001")),
6
};
static const SECItem fakeSerial0000 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("0000")),
4
};
TEST_F(OCSPCacheTest, TestPutAndGet)
{
static const SECItem fakeSerial000 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("000")),
3
};
static const SECItem fakeSerial001 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("001")),
3
};
SCOPED_TRACE("");
CERTCertificate subject;
MakeFakeCert(&subject, "CN=subject1", "CN=issuer1", "001", "key001");
CERTCertificate issuer;
MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
PutAndGet(cache, &subject, &issuer, 0, PR_Now());
PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial001), 0, PR_Now());
PRErrorCode errorOut;
PRTime timeOut;
ASSERT_FALSE(cache.Get(&issuer, &issuer, errorOut, timeOut));
ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial000),
errorOut, timeOut));
}
TEST_F(OCSPCacheTest, TestVariousGets)
{
SCOPED_TRACE("");
CERTCertificate issuer;
MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
PRTime timeIn = PR_Now();
for (int i = 0; i < MaxCacheEntries; i++) {
CERTCertificate subject;
char subjectBuf[64];
PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
char serialBuf[8];
PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
PutAndGet(cache, &subject, &issuer, 0, timeIn + i);
const SECItem fakeSerial = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(serialBuf)),
4
};
PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), 0, timeIn + i);
}
CERTCertificate subject;
// This will be at the end of the list in the cache
PRErrorCode errorOut;
PRTime timeOut;
MakeFakeCert(&subject, "CN=subject0000", "CN=issuer1", "0000", "key000");
ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
// This will be at the end of the list in the cache
CertID cert0000(fakeIssuer1, fakeKey000, fakeSerial0000);
ASSERT_TRUE(cache.Get(cert0000, errorOut, timeOut));
ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
// Once we access it, it goes to the front
ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
ASSERT_TRUE(cache.Get(cert0000, errorOut, timeOut));
ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
MakeFakeCert(&subject, "CN=subject0512", "CN=issuer1", "0512", "key000");
// This will be in the middle
ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
static const SECItem fakeSerial0512 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("0512")),
4
};
CertID cert0512(fakeIssuer1, fakeKey000, fakeSerial0512);
ASSERT_TRUE(cache.Get(cert0512, errorOut, timeOut));
ASSERT_TRUE(errorOut == 0 && timeOut == timeIn + 512);
ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
ASSERT_TRUE(cache.Get(cert0512, errorOut, timeOut));
ASSERT_TRUE(errorOut == 0 && timeOut == timeIn + 512);
// We've never seen this certificate
MakeFakeCert(&subject, "CN=subject1111", "CN=issuer1", "1111", "key000");
ASSERT_FALSE(cache.Get(&subject, &issuer, errorOut, timeOut));
static const SECItem fakeSerial1111 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("1111")),
4
};
ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey000, fakeSerial1111),
errorOut, timeOut));
}
TEST_F(OCSPCacheTest, TestEviction)
{
SCOPED_TRACE("");
CERTCertificate issuer;
PRTime timeIn = PR_Now();
MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
// 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++) {
CERTCertificate subject;
char subjectBuf[64];
PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
char serialBuf[8];
PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
PutAndGet(cache, &subject, &issuer, 0, timeIn + i);
const SECItem fakeSerial = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(serialBuf)),
4
};
PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), 0, timeIn + i);
}
CERTCertificate evictedSubject;
MakeFakeCert(&evictedSubject, "CN=subject0000", "CN=issuer1", "0000", "key000");
PRErrorCode errorOut;
PRTime timeOut;
ASSERT_FALSE(cache.Get(&evictedSubject, &issuer, errorOut, timeOut));
ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial0000),
errorOut, timeOut));
}
TEST_F(OCSPCacheTest, TestNoEvictionForRevokedResponses)
{
SCOPED_TRACE("");
CERTCertificate issuer;
MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
CERTCertificate notEvictedSubject;
MakeFakeCert(&notEvictedSubject, "CN=subject0000", "CN=issuer1", "0000", "key000");
PRTime timeIn = PR_Now();
PutAndGet(cache, &notEvictedSubject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE, timeIn);
CertID notEvicted(fakeIssuer1, fakeKey000, fakeSerial0000);
PutAndGet(cache, notEvicted, SEC_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++) {
CERTCertificate subject;
char subjectBuf[64];
PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
char serialBuf[8];
PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
PutAndGet(cache, &subject, &issuer, 0, timeIn + i);
const SECItem fakeSerial = {
siBuffer,
reinterpret_cast<uint8_t*>(serialBuf),
4
};
PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), 0, timeIn + i);
}
PRErrorCode errorOut;
PRTime timeOut;
ASSERT_TRUE(cache.Get(&notEvictedSubject, &issuer, errorOut, timeOut));
ASSERT_TRUE(cache.Get(notEvicted, errorOut, timeOut));
ASSERT_TRUE(errorOut == SEC_ERROR_REVOKED_CERTIFICATE && timeOut == timeIn);
CERTCertificate evictedSubject;
MakeFakeCert(&evictedSubject, "CN=subject0001", "CN=issuer1", "0001", "key000");
ASSERT_FALSE(cache.Get(&evictedSubject, &issuer, errorOut, timeOut));
const SECItem fakeSerial0001 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("0001")),
4
};
CertID evicted(fakeIssuer1, fakeKey000, fakeSerial0001);
ASSERT_FALSE(cache.Get(evicted, errorOut, timeOut));
}
TEST_F(OCSPCacheTest, TestEverythingIsRevoked)
{
SCOPED_TRACE("");
CERTCertificate issuer;
MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
PRTime timeIn = PR_Now();
// Fill up the cache with revoked responses.
for (int i = 0; i < MaxCacheEntries; i++) {
CERTCertificate subject;
char subjectBuf[64];
PR_snprintf(subjectBuf, sizeof(subjectBuf), "CN=subject%04d", i);
char serialBuf[8];
PR_snprintf(serialBuf, sizeof(serialBuf), "%04d", i);
MakeFakeCert(&subject, subjectBuf, "CN=issuer1", serialBuf, "key000");
PutAndGet(cache, &subject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE, timeIn + i);
const SECItem fakeSerial = {
siBuffer,
reinterpret_cast<uint8_t*>(serialBuf),
4
};
PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
SEC_ERROR_REVOKED_CERTIFICATE, timeIn + i);
}
CERTCertificate goodSubject;
MakeFakeCert(&goodSubject, "CN=subject1025", "CN=issuer1", "1025", "key000");
const SECItem fakeSerial1025 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("1025")),
4
};
CertID good(fakeIssuer1, fakeKey000, fakeSerial1025);
// This will "succeed", allowing verification to continue. However,
// nothing was actually put in the cache.
SECStatus result = cache.Put(&goodSubject, &issuer, 0, timeIn + 1025 - 50,
timeIn + 1025);
SECStatus result = cache.Put(good, 0, timeIn + 1025 - 50, timeIn + 1025);
ASSERT_TRUE(result == SECSuccess);
PRErrorCode errorOut;
PRTime timeOut;
ASSERT_FALSE(cache.Get(&goodSubject, &issuer, errorOut, timeOut));
ASSERT_FALSE(cache.Get(good, errorOut, timeOut));
CERTCertificate revokedSubject;
MakeFakeCert(&revokedSubject, "CN=subject1026", "CN=issuer1", "1026", "key000");
const SECItem fakeSerial1026 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("1026")),
4
};
CertID revoked(fakeIssuer1, fakeKey000, fakeSerial1026);
// This will fail, causing verification to fail.
result = cache.Put(&revokedSubject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE,
result = cache.Put(revoked, SEC_ERROR_REVOKED_CERTIFICATE,
timeIn + 1026 - 50, timeIn + 1026);
PRErrorCode error = PR_GetError();
ASSERT_TRUE(result == SECFailure);
@ -209,75 +240,76 @@ TEST_F(OCSPCacheTest, TestEverythingIsRevoked)
TEST_F(OCSPCacheTest, VariousIssuers)
{
SCOPED_TRACE("");
CERTCertificate issuer1;
MakeFakeCert(&issuer1, "CN=issuer1", "CN=issuer1", "000", "key000");
CERTCertificate issuer2;
MakeFakeCert(&issuer2, "CN=issuer2", "CN=issuer2", "000", "key001");
CERTCertificate issuer3;
// Note: same CN as issuer1
MakeFakeCert(&issuer3, "CN=issuer1", "CN=issuer3", "000", "key003");
CERTCertificate subject;
MakeFakeCert(&subject, "CN=subject", "CN=issuer1", "001", "key002");
static const SECItem fakeIssuer2 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("CN=issuer2")),
10
};
static const SECItem fakeSerial001 = {
siBuffer,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>("001")),
3
};
PRTime timeIn = PR_Now();
PutAndGet(cache, &subject, &issuer1, 0, timeIn);
CertID subject(fakeIssuer1, fakeKey000, fakeSerial001);
PutAndGet(cache, subject, 0, timeIn);
PRErrorCode errorOut;
PRTime timeOut;
ASSERT_TRUE(cache.Get(&subject, &issuer1, errorOut, timeOut));
ASSERT_TRUE(cache.Get(subject, errorOut, timeOut));
ASSERT_TRUE(errorOut == 0 && timeOut == timeIn);
ASSERT_FALSE(cache.Get(&subject, &issuer2, errorOut, timeOut));
ASSERT_FALSE(cache.Get(&subject, &issuer3, errorOut, timeOut));
// Test that we don't match a different issuer DN
ASSERT_FALSE(cache.Get(CertID(fakeIssuer2, fakeKey000, fakeSerial001),
errorOut, timeOut));
// Test that we don't match a different issuer key
ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial001),
errorOut, timeOut));
}
TEST_F(OCSPCacheTest, Times)
{
SCOPED_TRACE("");
CERTCertificate subject;
MakeFakeCert(&subject, "CN=subject1", "CN=issuer1", "001", "key001");
CERTCertificate issuer;
MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
PutAndGet(cache, &subject, &issuer, SEC_ERROR_OCSP_UNKNOWN_CERT, 100);
PutAndGet(cache, &subject, &issuer, 0, 200);
CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
PutAndGet(cache, certID, SEC_ERROR_OCSP_UNKNOWN_CERT, 100);
PutAndGet(cache, certID, 0, 200);
// This should not override the more recent entry.
SECStatus rv = cache.Put(&subject, &issuer, SEC_ERROR_OCSP_UNKNOWN_CERT, 100, 100);
ASSERT_TRUE(rv == SECSuccess);
ASSERT_EQ(SECSuccess, cache.Put(certID, SEC_ERROR_OCSP_UNKNOWN_CERT, 100,
100));
PRErrorCode errorOut;
PRTime timeOut;
ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
ASSERT_TRUE(cache.Get(certID, errorOut, timeOut));
// Here we see the more recent time.
ASSERT_TRUE(errorOut == 0 && timeOut == 200);
// SEC_ERROR_REVOKED_CERTIFICATE overrides everything
PutAndGet(cache, &subject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE, 50);
PutAndGet(cache, certID, SEC_ERROR_REVOKED_CERTIFICATE, 50);
}
TEST_F(OCSPCacheTest, NetworkFailure)
{
SCOPED_TRACE("");
CERTCertificate subject;
MakeFakeCert(&subject, "CN=subject1", "CN=issuer1", "001", "key001");
CERTCertificate issuer;
MakeFakeCert(&issuer, "CN=issuer1", "CN=issuer1", "000", "key000");
PutAndGet(cache, &subject, &issuer, PR_CONNECT_REFUSED_ERROR, 100);
PutAndGet(cache, &subject, &issuer, 0, 200);
CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
PutAndGet(cache, certID, PR_CONNECT_REFUSED_ERROR, 100);
PutAndGet(cache, certID, 0, 200);
// This should not override the already present entry.
SECStatus rv = cache.Put(&subject, &issuer, PR_CONNECT_REFUSED_ERROR, 300, 350);
SECStatus rv = cache.Put(certID, PR_CONNECT_REFUSED_ERROR, 300, 350);
ASSERT_TRUE(rv == SECSuccess);
PRErrorCode errorOut;
PRTime timeOut;
ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
ASSERT_TRUE(cache.Get(certID, errorOut, timeOut));
ASSERT_TRUE(errorOut == 0 && timeOut == 200);
PutAndGet(cache, &subject, &issuer, SEC_ERROR_OCSP_UNKNOWN_CERT, 400);
PutAndGet(cache, certID, SEC_ERROR_OCSP_UNKNOWN_CERT, 400);
// This should not override the already present entry.
rv = cache.Put(&subject, &issuer, PR_CONNECT_REFUSED_ERROR, 500, 550);
rv = cache.Put(certID, PR_CONNECT_REFUSED_ERROR, 500, 550);
ASSERT_TRUE(rv == SECSuccess);
ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
ASSERT_TRUE(cache.Get(certID, errorOut, timeOut));
ASSERT_TRUE(errorOut == SEC_ERROR_OCSP_UNKNOWN_CERT && timeOut == 400);
PutAndGet(cache, &subject, &issuer, SEC_ERROR_REVOKED_CERTIFICATE, 600);
PutAndGet(cache, certID, SEC_ERROR_REVOKED_CERTIFICATE, 600);
// This should not override the already present entry.
rv = cache.Put(&subject, &issuer, PR_CONNECT_REFUSED_ERROR, 700, 750);
rv = cache.Put(certID, PR_CONNECT_REFUSED_ERROR, 700, 750);
ASSERT_TRUE(rv == SECSuccess);
ASSERT_TRUE(cache.Get(&subject, &issuer, errorOut, timeOut));
ASSERT_TRUE(cache.Get(certID, errorOut, timeOut));
ASSERT_TRUE(errorOut == SEC_ERROR_REVOKED_CERTIFICATE && timeOut == 600);
}

View File

@ -6,16 +6,16 @@
#include <stdio.h>
#include "pkixtestutil.h"
#include "ScopedNSSTypes.h"
#include "TLSServer.h"
#include "pkixtestutil.h"
#include "secder.h"
#include "secerr.h"
using namespace mozilla;
using namespace mozilla::test;
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
using namespace mozilla::test;
SECItemArray *
GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert,
@ -40,25 +40,26 @@ GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert,
PRTime oneDay = 60*60*24 * (PRTime)PR_USEC_PER_SEC;
PRTime oldNow = now - (8 * oneDay);
OCSPResponseContext context(aArena, aCert, now);
mozilla::ScopedCERTCertificate cert(CERT_DupCertificate(aCert));
if (aORT == ORTGoodOtherCert) {
context.cert = PK11_FindCertFromNickname(aAdditionalCertName, nullptr);
if (!context.cert) {
cert = PK11_FindCertFromNickname(aAdditionalCertName, nullptr);
if (!cert) {
PrintPRError("PK11_FindCertFromNickname failed");
return nullptr;
}
}
// XXX CERT_FindCertIssuer uses the old, deprecated path-building logic
ScopedCERTCertificate issuerCert(CERT_FindCertIssuer(aCert, now,
certUsageSSLCA));
mozilla::ScopedCERTCertificate
issuerCert(CERT_FindCertIssuer(aCert, now, certUsageSSLCA));
if (!issuerCert) {
PrintPRError("CERT_FindCertIssuer failed");
return nullptr;
}
context.issuerNameDER = &issuerCert->derSubject;
context.issuerSPKI = &issuerCert->subjectPublicKeyInfo;
ScopedCERTCertificate signerCert;
CertID certID(cert->derIssuer, issuerCert->derPublicKey, cert->serialNumber);
OCSPResponseContext context(aArena, certID, now);
mozilla::ScopedCERTCertificate signerCert;
if (aORT == ORTGoodOtherCA || aORT == ORTDelegatedIncluded ||
aORT == ORTDelegatedIncludedLast || aORT == ORTDelegatedMissing ||
aORT == ORTDelegatedMissingMultiple) {
@ -77,7 +78,7 @@ GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert,
}
if (aORT == ORTDelegatedIncludedLast || aORT == ORTDelegatedMissingMultiple) {
certs[0] = &issuerCert->derCert;
certs[1] = &context.cert->derCert;
certs[1] = &cert->derCert;
certs[2] = &issuerCert->derCert;
if (aORT != ORTDelegatedMissingMultiple) {
certs[3] = &signerCert->derCert;

View File

@ -105,9 +105,7 @@ SECStatus VerifySignedData(const CERTSignedData* sd,
void* pkcs11PinArg);
// The return value, if non-null, is owned by the arena and MUST NOT be freed.
SECItem* CreateEncodedOCSPRequest(PLArenaPool* arena,
const CERTCertificate* cert,
const CERTCertificate* issuerCert);
SECItem* CreateEncodedOCSPRequest(PLArenaPool* arena, const CertID& certID);
// The out parameter expired will be true if the response has expired. If the
// response also indicates a revoked or unknown certificate, that error
@ -121,14 +119,12 @@ SECItem* CreateEncodedOCSPRequest(PLArenaPool* arena,
// which the encoded response is considered trustworthy (that is, if a response had a
// thisUpdate time of validThrough, it would be considered trustworthy).
SECStatus VerifyEncodedOCSPResponse(TrustDomain& trustDomain,
const CERTCertificate* cert,
CERTCertificate* issuerCert,
PRTime time,
const CertID& certID, PRTime time,
uint16_t maxLifetimeInDays,
const SECItem* encodedResponse,
const SECItem& encodedResponse,
/* out */ bool& expired,
/* optional out */ PRTime* thisUpdate,
/* optional out */ PRTime* validThrough);
/* optional out */ PRTime* thisUpdate = nullptr,
/* optional out */ PRTime* validThrough = nullptr);
} } // namespace mozilla::pkix

View File

@ -25,11 +25,11 @@
#ifndef mozilla_pkix__pkixtypes_h
#define mozilla_pkix__pkixtypes_h
#include "cert.h"
#include "pkix/enumclass.h"
#include "pkix/ScopedPtr.h"
#include "plarena.h"
#include "cert.h"
#include "keyhi.h"
#include "seccomon.h"
#include "secport.h"
#include "stdint.h"
namespace mozilla { namespace pkix {
@ -88,6 +88,34 @@ MOZILLA_PKIX_ENUM_CLASS TrustLevel {
InheritsTrust = 3 // certificate must chain to a trust anchor
};
// CertID references the information needed to do revocation checking for the
// certificate issued by the given issuer with the given serial number.
//
// issuer must be the DER-encoded issuer field from the certificate for which
// revocation checking is being done, **NOT** the subject field of the issuer
// certificate. (Those two fields must be equal to each other, but they may not
// be encoded exactly the same, and the encoding matters for OCSP.)
// issuerSubjectPublicKeyInfo is the entire DER-encoded subjectPublicKeyInfo
// field from the issuer's certificate. serialNumber is the entire DER-encoded
// serial number from the subject certificate (the certificate for which we are
// checking the revocation status).
struct CertID
{
public:
CertID(const SECItem& issuer, const SECItem& issuerSubjectPublicKeyInfo,
const SECItem& serialNumber)
: issuer(issuer)
, issuerSubjectPublicKeyInfo(issuerSubjectPublicKeyInfo)
, serialNumber(serialNumber)
{
}
const SECItem& issuer;
const SECItem& issuerSubjectPublicKeyInfo;
const SECItem& serialNumber;
private:
void operator=(const CertID&) /*= delete*/;
};
// Applications control the behavior of path building and verification by
// implementing the TrustDomain interface. The TrustDomain is used for all
// cryptography and for determining which certificates are trusted or
@ -136,10 +164,9 @@ public:
// issuerCertToDup is only non-const so CERT_DupCertificate can be called on
// it.
virtual SECStatus CheckRevocation(EndEntityOrCA endEntityOrCA,
const CERTCertificate* cert,
/*const*/ CERTCertificate* issuerCertToDup,
PRTime time,
/*optional*/ const SECItem* stapledOCSPresponse) = 0;
const CertID& certID, PRTime time,
/*optional*/ const SECItem* stapledOCSPresponse,
/*optional*/ const SECItem* aiaExtension) = 0;
// Called as soon as we think we have a valid chain but before revocation
// checks are done. Called to compute additional chain level checks, by the

View File

@ -69,7 +69,6 @@ BackCert::Init(const SECItem& certDER)
const SECItem* dummyEncodedSubjectKeyIdentifier = nullptr;
const SECItem* dummyEncodedAuthorityKeyIdentifier = nullptr;
const SECItem* dummyEncodedAuthorityInfoAccess = nullptr;
const SECItem* dummyEncodedSubjectAltName = nullptr;
for (const CERTCertExtension* ext = *exts; ext; ext = *++exts) {
@ -104,7 +103,7 @@ BackCert::Init(const SECItem& certDER)
// We should remember the value of the encoded AIA extension here, but
// since our TrustDomain implementations get the OCSP URI using
// CERT_GetOCSPAuthorityInfoAccessLocation, we currently don't need to.
out = &dummyEncodedAuthorityInfoAccess;
out = &encodedAuthorityInfoAccess;
}
// If this is an extension we don't understand and it's marked critical,
@ -283,7 +282,7 @@ BuildForward(TrustDomain& trustDomain,
// Find a trusted issuer.
// TODO(bug 965136): Add SKI/AKI matching optimizations
ScopedCERTCertList candidates;
if (trustDomain.FindPotentialIssuers(&subject.GetNSSCert()->derIssuer, time,
if (trustDomain.FindPotentialIssuers(&subject.GetIssuer(), time,
candidates) != SECSuccess) {
return MapSECStatus(SECFailure);
}
@ -305,10 +304,12 @@ BuildForward(TrustDomain& trustDomain,
return Fail(FatalError, deferredEndEntityError);
}
SECStatus srv = trustDomain.CheckRevocation(endEntityOrCA,
subject.GetNSSCert(),
n->cert, time,
stapledOCSPResponse);
CertID certID(subject.GetIssuer(), n->cert->derPublicKey,
subject.GetSerialNumber());
SECStatus srv = trustDomain.CheckRevocation(
endEntityOrCA, certID, time,
stapledOCSPResponse,
subject.encodedAuthorityInfoAccess);
if (srv != SECSuccess) {
return MapSECStatus(SECFailure);
}

View File

@ -22,13 +22,13 @@
* limitations under the License.
*/
#include "pkix/pkix.h"
#include <limits>
#include <stdint.h>
#include "cert.h"
#include "cryptohi.h"
#include "keyhi.h"
#include "pkix/pkix.h"
#include "prerror.h"
#include "secerr.h"

View File

@ -24,15 +24,13 @@
#include <limits>
#include "hasht.h"
#include "pk11pub.h"
#include "pkix/bind.h"
#include "pkix/pkix.h"
#include "pkixcheck.h"
#include "pkixder.h"
#include "hasht.h"
#include "pk11pub.h"
#include "secder.h"
// TODO: use typed/qualified typedefs everywhere?
// TODO: When should we return SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE?
@ -52,18 +50,11 @@ MOZILLA_PKIX_ENUM_CLASS CertStatus : uint8_t {
class Context
{
public:
Context(TrustDomain& trustDomain,
const SECItem& certSerialNumber,
const SECItem& issuerSubject,
const SECItem& issuerSubjectPublicKeyInfo,
PRTime time,
uint16_t maxLifetimeInDays,
PRTime* thisUpdate,
PRTime* validThrough)
Context(TrustDomain& trustDomain, const CertID& certID, PRTime time,
uint16_t maxLifetimeInDays, /*optional out*/ PRTime* thisUpdate,
/*optional out*/ PRTime* validThrough)
: trustDomain(trustDomain)
, certSerialNumber(certSerialNumber)
, issuerSubject(issuerSubject)
, issuerSubjectPublicKeyInfo(issuerSubjectPublicKeyInfo)
, certID(certID)
, time(time)
, maxLifetimeInDays(maxLifetimeInDays)
, certStatus(CertStatus::Unknown)
@ -80,9 +71,7 @@ public:
}
TrustDomain& trustDomain;
const SECItem& certSerialNumber;
const SECItem& issuerSubject;
const SECItem& issuerSubjectPublicKeyInfo;
const CertID& certID;
const PRTime time;
const uint16_t maxLifetimeInDays;
CertStatus certStatus;
@ -197,6 +186,9 @@ static inline der::Result CertID(der::Input& input,
static Result MatchKeyHash(const SECItem& issuerKeyHash,
const SECItem& issuerSubjectPublicKeyInfo,
/*out*/ bool& match);
static Result KeyHash(const SECItem& subjectPublicKeyInfo,
/*out*/ uint8_t* hashBuf, size_t hashBufSize);
static Result
MatchResponderID(ResponderIDType responderIDType,
@ -261,14 +253,15 @@ VerifySignature(Context& context, ResponderIDType responderIDType,
{
bool match;
Result rv = MatchResponderID(responderIDType, responderID,
context.issuerSubject,
context.issuerSubjectPublicKeyInfo, match);
context.certID.issuer,
context.certID.issuerSubjectPublicKeyInfo,
match);
if (rv != Success) {
return rv;
}
if (match) {
return VerifyOCSPSignedData(context.trustDomain, signedResponseData,
context.issuerSubjectPublicKeyInfo);
context.certID.issuerSubjectPublicKeyInfo);
}
for (size_t i = 0; i < numCerts; ++i) {
@ -289,8 +282,8 @@ VerifySignature(Context& context, ResponderIDType responderIDType,
if (match) {
rv = CheckOCSPResponseSignerCert(context.trustDomain, cert,
context.issuerSubject,
context.issuerSubjectPublicKeyInfo,
context.certID.issuer,
context.certID.issuerSubjectPublicKeyInfo,
context.time);
if (rv == FatalError) {
return rv;
@ -316,34 +309,22 @@ SetErrorToMalformedResponseOnBadDERError()
}
SECStatus
VerifyEncodedOCSPResponse(TrustDomain& trustDomain,
const CERTCertificate* cert,
CERTCertificate* issuerCert, PRTime time,
uint16_t maxOCSPLifetimeInDays,
const SECItem* encodedResponse,
VerifyEncodedOCSPResponse(TrustDomain& trustDomain, const struct CertID& certID,
PRTime time, uint16_t maxOCSPLifetimeInDays,
const SECItem& encodedResponse,
bool& expired,
PRTime* thisUpdate,
PRTime* validThrough)
/*optional out*/ PRTime* thisUpdate,
/*optional out*/ PRTime* validThrough)
{
PR_ASSERT(cert);
PR_ASSERT(issuerCert);
// TODO: PR_Assert(pinArg)
PR_ASSERT(encodedResponse);
if (!cert || !issuerCert || !encodedResponse || !encodedResponse->data) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
}
// Always initialize this to something reasonable.
expired = false;
der::Input input;
if (input.Init(encodedResponse->data, encodedResponse->len) != der::Success) {
if (input.Init(encodedResponse.data, encodedResponse.len) != der::Success) {
SetErrorToMalformedResponseOnBadDERError();
return SECFailure;
}
Context context(trustDomain, cert->serialNumber, issuerCert->derSubject,
issuerCert->derPublicKey, time, maxOCSPLifetimeInDays,
Context context(trustDomain, certID, time, maxOCSPLifetimeInDays,
thisUpdate, validThrough);
if (der::Nested(input, der::SEQUENCE,
@ -726,7 +707,7 @@ CertID(der::Input& input, const Context& context, /*out*/ bool& match)
return der::Failure;
}
if (!SECITEM_ItemsAreEqual(&serialNumber, &context.certSerialNumber)) {
if (!SECITEM_ItemsAreEqual(&serialNumber, &context.certID.serialNumber)) {
// This does not reference the certificate we're interested in.
// Consume the rest of the input and return successfully to
// potentially continue processing other responses.
@ -751,7 +732,7 @@ CertID(der::Input& input, const Context& context, /*out*/ bool& match)
// "The hash shall be calculated over the DER encoding of the
// issuer's name field in the certificate being checked."
uint8_t hashBuf[SHA1_LENGTH];
if (HashBuf(context.issuerSubject, hashBuf, sizeof(hashBuf))
if (HashBuf(context.certID.issuer, hashBuf, sizeof(hashBuf))
!= der::Success) {
return der::Failure;
}
@ -761,7 +742,7 @@ CertID(der::Input& input, const Context& context, /*out*/ bool& match)
return der::Success;
}
if (MatchKeyHash(issuerKeyHash, context.issuerSubjectPublicKeyInfo,
if (MatchKeyHash(issuerKeyHash, context.certID.issuerSubjectPublicKeyInfo,
match) != Success) {
return der::Failure;
}
@ -786,8 +767,23 @@ MatchKeyHash(const SECItem& keyHash, const SECItem& subjectPublicKeyInfo,
if (keyHash.len != SHA1_LENGTH) {
return Fail(RecoverableError, SEC_ERROR_OCSP_MALFORMED_RESPONSE);
}
static uint8_t hashBuf[SHA1_LENGTH];
Result rv = KeyHash(subjectPublicKeyInfo, hashBuf, sizeof hashBuf);
if (rv != Success) {
return rv;
}
match = !memcmp(hashBuf, keyHash.data, keyHash.len);
return Success;
}
// TODO(bug 966856): support SHA-2 hashes
// TODO(bug 966856): support SHA-2 hashes
Result
KeyHash(const SECItem& subjectPublicKeyInfo, /*out*/ uint8_t* hashBuf,
size_t hashBufSize)
{
if (!hashBuf || hashBufSize != SHA1_LENGTH) {
return Fail(FatalError, SEC_ERROR_LIBRARY_FAILURE);
}
// RFC 5280 Section 4.1
//
@ -836,11 +832,9 @@ MatchKeyHash(const SECItem& keyHash, const SECItem& subjectPublicKeyInfo,
++subjectPublicKey.data;
--subjectPublicKey.len;
static uint8_t hashBuf[SHA1_LENGTH];
if (HashBuf(subjectPublicKey, hashBuf, sizeof(hashBuf)) != der::Success) {
if (HashBuf(subjectPublicKey, hashBuf, hashBufSize) != der::Success) {
return MapSECStatus(SECFailure);
}
match = !memcmp(hashBuf, keyHash.data, keyHash.len);
return Success;
}
@ -903,11 +897,9 @@ CheckExtensionsForCriticality(der::Input& input)
// http://tools.ietf.org/html/rfc5019#section-4
SECItem*
CreateEncodedOCSPRequest(PLArenaPool* arena,
const CERTCertificate* cert,
const CERTCertificate* issuerCert)
CreateEncodedOCSPRequest(PLArenaPool* arena, const struct CertID& certID)
{
if (!arena || !cert || !issuerCert) {
if (!arena) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return nullptr;
}
@ -952,13 +944,13 @@ CreateEncodedOCSPRequest(PLArenaPool* arena,
// we allow for some amount of non-conformance with that requirement while
// still ensuring we can encode the length values in the ASN.1 TLV structures
// in a single byte.
if (cert->serialNumber.len > 127u - totalLenWithoutSerialNumberData) {
if (certID.serialNumber.len > 127u - totalLenWithoutSerialNumberData) {
PR_SetError(SEC_ERROR_BAD_DATA, 0);
return nullptr;
}
uint8_t totalLen = static_cast<uint8_t>(totalLenWithoutSerialNumberData +
cert->serialNumber.len);
certID.serialNumber.len);
SECItem* encodedRequest = SECITEM_AllocItem(arena, nullptr, totalLen);
if (!encodedRequest) {
@ -980,7 +972,7 @@ CreateEncodedOCSPRequest(PLArenaPool* arena,
// reqCert.issuerNameHash (OCTET STRING)
*d++ = 0x04;
*d++ = hashLen;
if (HashBuf(issuerCert->derSubject, d, hashLen) != der::Success) {
if (HashBuf(certID.issuer, d, hashLen) != der::Success) {
return nullptr;
}
d += hashLen;
@ -988,18 +980,16 @@ CreateEncodedOCSPRequest(PLArenaPool* arena,
// reqCert.issuerKeyHash (OCTET STRING)
*d++ = 0x04;
*d++ = hashLen;
SECItem key = issuerCert->subjectPublicKeyInfo.subjectPublicKey;
DER_ConvertBitString(&key);
if (HashBuf(key, d, hashLen) != der::Success) {
if (KeyHash(certID.issuerSubjectPublicKeyInfo, d, hashLen) != Success) {
return nullptr;
}
d += hashLen;
// reqCert.serialNumber (INTEGER)
*d++ = 0x02; // INTEGER
*d++ = static_cast<uint8_t>(cert->serialNumber.len);
for (size_t i = 0; i < cert->serialNumber.len; ++i) {
*d++ = cert->serialNumber.data[i];
*d++ = static_cast<uint8_t>(certID.serialNumber.len);
for (size_t i = 0; i < certID.serialNumber.len; ++i) {
*d++ = certID.serialNumber.data[i];
}
PR_ASSERT(d == encodedRequest->data + totalLen);

View File

@ -96,7 +96,8 @@ public:
// nssCert and childCert must be valid for the lifetime of BackCert
BackCert(BackCert* childCert, IncludeCN includeCN)
: encodedBasicConstraints(nullptr)
: encodedAuthorityInfoAccess(nullptr)
, encodedBasicConstraints(nullptr)
, encodedCertificatePolicies(nullptr)
, encodedExtendedKeyUsage(nullptr)
, encodedKeyUsage(nullptr)
@ -112,6 +113,7 @@ public:
const SECItem& GetDER() const { return nssCert->derCert; }
const SECItem& GetIssuer() const { return nssCert->derIssuer; }
const SECItem& GetSerialNumber() const { return nssCert->serialNumber; }
const SECItem& GetSubject() const { return nssCert->derSubject; }
const SECItem& GetSubjectPublicKeyInfo() const
{
@ -121,6 +123,7 @@ public:
Result VerifyOwnSignatureWithKey(TrustDomain& trustDomain,
const SECItem& subjectPublicKeyInfo) const;
const SECItem* encodedAuthorityInfoAccess;
const SECItem* encodedBasicConstraints;
const SECItem* encodedCertificatePolicies;
const SECItem* encodedExtendedKeyUsage;

View File

@ -142,8 +142,8 @@ private:
nullptr);
}
SECStatus CheckRevocation(EndEntityOrCA, const CERTCertificate*,
/*const*/ CERTCertificate*, PRTime,
SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
/*optional*/ const SECItem*,
/*optional*/ const SECItem*)
{
return SECSuccess;

View File

@ -95,8 +95,8 @@ private:
nullptr);
}
SECStatus CheckRevocation(EndEntityOrCA, const CERTCertificate*,
/*const*/ CERTCertificate*, PRTime,
SECStatus CheckRevocation(EndEntityOrCA, const CertID&, PRTime,
/*optional*/ const SECItem*,
/*optional*/ const SECItem*)
{
return SECSuccess;

View File

@ -36,7 +36,6 @@ class pkix_ocsp_request_tests : public NSSTest
protected:
// These SECItems are allocated in arena, and so will be auto-cleaned.
SECItem* unsupportedLongSerialNumber;
SECItem* shortSerialNumber;
SECItem* longestRequiredSerialNumber;
void SetUp()
@ -54,12 +53,6 @@ protected:
unsupportedLongSerialNumber->data[2] = UNSUPPORTED_LEN;
unsupportedLongSerialNumber->data[3] = 0x01; // value is 0x010000...00
// Each of tag, length, and value here are 1 byte: the total length is 3.
shortSerialNumber = SECITEM_AllocItem(arena.get(), nullptr, 3);
shortSerialNumber->data[0] = der::INTEGER;
shortSerialNumber->data[1] = 0x01; // length of value is 1
shortSerialNumber->data[2] = 0x01; // value is 1
static const uint8_t LONGEST_REQUIRED_LEN = 20;
// tag + length + value is 1 + 1 + LONGEST_REQUIRED_LEN
longestRequiredSerialNumber = SECITEM_AllocItem(arena.get(), nullptr,
@ -71,74 +64,41 @@ protected:
longestRequiredSerialNumber->data[2] = 0x01; // value is 0x010000...00
}
void MakeTwoCerts(const char* issuerCN, SECItem* issuerSerial,
/*out*/ ScopedCERTCertificate& issuer,
const char* childCN, SECItem* childSerial,
/*out*/ ScopedCERTCertificate& child)
// The resultant issuerDER is owned by the arena.
SECStatus MakeIssuerCertIDComponents(const char* issuerASCII,
/*out*/ const SECItem*& issuerDER,
/*out*/ ScopedSECItem& issuerSPKI)
{
const SECItem* issuerNameDer = ASCIIToDERName(arena.get(), issuerCN);
ASSERT_TRUE(issuerNameDer);
ScopedSECKEYPrivateKey issuerKey;
SECItem* issuerCertDer(CreateEncodedCertificate(arena.get(), v3,
SEC_OID_SHA256, issuerSerial, issuerNameDer,
oneDayBeforeNow, oneDayAfterNow, issuerNameDer,
nullptr, nullptr, SEC_OID_SHA256, issuerKey));
ASSERT_TRUE(issuerCertDer);
const SECItem* childNameDer = ASCIIToDERName(arena.get(), childCN);
ASSERT_TRUE(childNameDer);
ScopedSECKEYPrivateKey childKey;
SECItem* childDer(CreateEncodedCertificate(arena.get(), v3,
SEC_OID_SHA256, childSerial, issuerNameDer,
oneDayBeforeNow, oneDayAfterNow, childNameDer, nullptr,
issuerKey.get(), SEC_OID_SHA256, childKey));
ASSERT_TRUE(childDer);
issuer = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), issuerCertDer,
nullptr, false, true);
ASSERT_TRUE(issuer);
child = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), childDer, nullptr,
false, true);
ASSERT_TRUE(child);
issuerDER = ASCIIToDERName(arena.get(), issuerASCII);
if (!issuerDER) {
return SECFailure;
}
ScopedSECKEYPublicKey issuerPublicKey;
ScopedSECKEYPrivateKey issuerPrivateKey;
if (GenerateKeyPair(issuerPublicKey, issuerPrivateKey) != SECSuccess) {
return SECFailure;
}
issuerSPKI = SECKEY_EncodeDERSubjectPublicKeyInfo(issuerPublicKey.get());
if (!issuerSPKI) {
return SECFailure;
}
return SECSuccess;
}
};
// Test that the large length of the issuer serial number doesn't cause
// CreateEncodedOCSPRequest to fail when called for the child certificate.
TEST_F(pkix_ocsp_request_tests, IssuerCertLongSerialNumberTest)
{
const char* issuerCN = "CN=Long Serial Number CA";
const char* childCN = "CN=Short Serial Number EE";
ScopedCERTCertificate issuer;
ScopedCERTCertificate child;
{
SCOPED_TRACE("IssuerCertLongSerialNumberTest");
MakeTwoCerts(issuerCN, unsupportedLongSerialNumber, issuer,
childCN, shortSerialNumber, child);
}
ASSERT_TRUE(issuer);
ASSERT_TRUE(child);
ASSERT_TRUE(CreateEncodedOCSPRequest(arena.get(), child.get(),
issuer.get()));
ASSERT_EQ(0, PR_GetError());
}
// Test that the large length of the child serial number causes
// CreateEncodedOCSPRequest to fail.
TEST_F(pkix_ocsp_request_tests, ChildCertLongSerialNumberTest)
{
const char* issuerCN = "CN=Short Serial Number CA";
const char* childCN = "CN=Long Serial Number EE";
ScopedCERTCertificate issuer;
ScopedCERTCertificate child;
{
SCOPED_TRACE("ChildCertLongSerialNumberTest");
MakeTwoCerts(issuerCN, shortSerialNumber, issuer,
childCN, unsupportedLongSerialNumber, child);
}
ASSERT_TRUE(issuer);
ASSERT_TRUE(child);
ASSERT_FALSE(CreateEncodedOCSPRequest(arena.get(), child.get(),
issuer.get()));
const SECItem* issuerDER;
ScopedSECItem issuerSPKI;
ASSERT_EQ(SECSuccess,
MakeIssuerCertIDComponents("CN=CA", issuerDER, issuerSPKI));
ASSERT_FALSE(CreateEncodedOCSPRequest(arena.get(),
CertID(*issuerDER, *issuerSPKI,
*unsupportedLongSerialNumber)));
ASSERT_EQ(SEC_ERROR_BAD_DATA, PR_GetError());
}
@ -146,18 +106,11 @@ TEST_F(pkix_ocsp_request_tests, ChildCertLongSerialNumberTest)
// it's required to support (i.e. 20 octets).
TEST_F(pkix_ocsp_request_tests, LongestSupportedSerialNumberTest)
{
const char* issuerCN = "CN=Short Serial Number CA";
const char* childCN = "CN=Longest Serial Number Supported EE";
ScopedCERTCertificate issuer;
ScopedCERTCertificate child;
{
SCOPED_TRACE("LongestSupportedSerialNumberTest");
MakeTwoCerts(issuerCN, shortSerialNumber, issuer,
childCN, longestRequiredSerialNumber, child);
}
ASSERT_TRUE(issuer);
ASSERT_TRUE(child);
ASSERT_TRUE(CreateEncodedOCSPRequest(arena.get(), child.get(),
issuer.get()));
ASSERT_EQ(0, PR_GetError());
const SECItem* issuerDER;
ScopedSECItem issuerSPKI;
ASSERT_EQ(SECSuccess,
MakeIssuerCertIDComponents("CN=CA", issuerDER, issuerSPKI));
ASSERT_TRUE(CreateEncodedOCSPRequest(arena.get(),
CertID(*issuerDER, *issuerSPKI,
*longestRequiredSerialNumber)));
}

View File

@ -178,14 +178,11 @@ private:
};
OCSPResponseContext::OCSPResponseContext(PLArenaPool* arena,
CERTCertificate* cert,
PRTime time)
const CertID& certID, PRTime time)
: arena(arena)
, cert(CERT_DupCertificate(cert))
, certID(certID)
, responseStatus(successful)
, skipResponseBytes(false)
, issuerNameDER(nullptr)
, issuerSPKI(nullptr)
, signerNameDER(nullptr)
, producedAt(time)
, extensions(nullptr)
@ -243,7 +240,7 @@ HashAlgorithmToLength(SECOidTag hashAlg)
}
static SECItem*
HashedOctetString(PLArenaPool* arena, const SECItem* bytes, SECOidTag hashAlg)
HashedOctetString(PLArenaPool* arena, const SECItem& bytes, SECOidTag hashAlg)
{
size_t hashLen = HashAlgorithmToLength(hashAlg);
if (hashLen == 0) {
@ -253,7 +250,7 @@ HashedOctetString(PLArenaPool* arena, const SECItem* bytes, SECOidTag hashAlg)
if (!hashBuf) {
return nullptr;
}
if (PK11_HashBuf(hashAlg, hashBuf->data, bytes->data, bytes->len)
if (PK11_HashBuf(hashAlg, hashBuf->data, bytes.data, bytes.len)
!= SECSuccess) {
return nullptr;
}
@ -267,7 +264,7 @@ KeyHashHelper(PLArenaPool* arena, const CERTSubjectPublicKeyInfo* spki)
// We only need a shallow copy here.
SECItem spk = spki->subjectPublicKey;
DER_ConvertBitString(&spk); // bits to bytes
return HashedOctetString(arena, &spk, SEC_OID_SHA1);
return HashedOctetString(arena, spk, SEC_OID_SHA1);
}
static SECItem*
@ -919,8 +916,7 @@ CreateEncodedOCSPResponse(OCSPResponseContext& context)
}
if (!context.skipResponseBytes) {
if (!context.cert || !context.issuerNameDER || !context.issuerSPKI ||
!context.signerPrivateKey) {
if (!context.signerPrivateKey) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return nullptr;
}
@ -1260,21 +1256,29 @@ CertID(OCSPResponseContext& context)
return nullptr;
}
SECItem* issuerNameHash = HashedOctetString(context.arena,
context.issuerNameDER,
context.certID.issuer,
context.certIDHashAlg);
if (!issuerNameHash) {
return nullptr;
}
SECItem* issuerKeyHash = KeyHashHelper(context.arena, context.issuerSPKI);
ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
spki(SECKEY_DecodeDERSubjectPublicKeyInfo(
&context.certID.issuerSubjectPublicKeyInfo));
if (!spki) {
return nullptr;
}
SECItem* issuerKeyHash(KeyHashHelper(context.arena, spki.get()));
if (!issuerKeyHash) {
return nullptr;
}
static const SEC_ASN1Template serialTemplate[] = {
{ SEC_ASN1_INTEGER, offsetof(CERTCertificate, serialNumber) },
{ SEC_ASN1_INTEGER, 0 },
{ 0 }
};
SECItem* serialNumber = SEC_ASN1EncodeItem(context.arena, nullptr,
context.cert.get(),
&context.certID.serialNumber,
serialTemplate);
if (!serialNumber) {
return nullptr;

View File

@ -28,6 +28,7 @@
#include <stdint.h>
#include <stdio.h>
#include "keyhi.h"
#include "pkix/enumclass.h"
#include "pkix/pkixtypes.h"
#include "pkix/ScopedPtr.h"
@ -128,11 +129,11 @@ public:
class OCSPResponseContext
{
public:
OCSPResponseContext(PLArenaPool* arena, CERTCertificate* cert, PRTime time);
OCSPResponseContext(PLArenaPool* arena, const CertID& certID, PRTime time);
PLArenaPool* arena;
const CertID& certID;
// TODO(bug 980538): add a way to specify what certificates are included.
pkix::ScopedCERTCertificate cert; // The subject of the OCSP response
// The fields below are in the order that they appear in an OCSP response.
@ -160,8 +161,6 @@ public:
bool skipResponseBytes; // If true, don't include responseBytes
// responderID
const SECItem* issuerNameDER; // non-owning
const CERTSubjectPublicKeyInfo* issuerSPKI; // non-owning pointer
const SECItem* signerNameDER; // If set, responderID will use the byName
// form; otherwise responderID will use the
// byKeyHash form.