Bug 1275238 - Certificate Transparency support in mozilla::pkix; r=keeler

MozReview-Commit-ID: HZwzSgxarTw

--HG--
extra : transplant_source : %BF%F9%A8T%C6x%82%03%3Ez%9F%3BT%E3%1B%11s%294%F4
This commit is contained in:
Sergei Chernov 2016-06-15 11:11:00 +03:00
Родитель 53c1367728
Коммит edb1f658f6
19 изменённых файлов: 364 добавлений и 19 удалений

Просмотреть файл

@ -378,4 +378,10 @@ AppTrustDomain::NetscapeStepUpMatchesServerAuth(Time /*notBefore*/,
return Success;
}
void
AppTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/,
Input /*extensionData*/)
{
}
} } // namespace mozilla::psm

Просмотреть файл

@ -64,6 +64,9 @@ public:
virtual Result NetscapeStepUpMatchesServerAuth(
mozilla::pkix::Time notBefore,
/*out*/ bool& matches) override;
virtual void NoteAuxiliaryExtension(
mozilla::pkix::AuxiliaryExtension extension,
mozilla::pkix::Input extensionData) override;
virtual Result DigestBuf(mozilla::pkix::Input item,
mozilla::pkix::DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf,

Просмотреть файл

@ -958,6 +958,12 @@ NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
void
NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/,
Input /*extensionData*/)
{
}
SECStatus
InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules)
{

Просмотреть файл

@ -141,6 +141,10 @@ public:
virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
mozilla::pkix::Time time) override;
virtual void NoteAuxiliaryExtension(
mozilla::pkix::AuxiliaryExtension extension,
mozilla::pkix::Input extensionData) override;
CertVerifier::OCSPStaplingStatus GetOCSPStaplingStatus() const
{
return mOCSPStaplingStatus;

Просмотреть файл

@ -108,6 +108,13 @@ OCSPVerificationTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
return mCertDBTrustDomain.NetscapeStepUpMatchesServerAuth(notBefore, matches);
}
void
OCSPVerificationTrustDomain::NoteAuxiliaryExtension(
AuxiliaryExtension extension, Input extensionData)
{
mCertDBTrustDomain.NoteAuxiliaryExtension(extension, extensionData);
}
Result
OCSPVerificationTrustDomain::DigestBuf(
Input item, DigestAlgorithm digestAlg,

Просмотреть файл

@ -73,6 +73,10 @@ public:
virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
mozilla::pkix::Time time) override;
virtual void NoteAuxiliaryExtension(
mozilla::pkix::AuxiliaryExtension extension,
mozilla::pkix::Input extensionData) override;
private:
NSSCertDBTrustDomain& mCertDBTrustDomain;
};

Просмотреть файл

@ -215,6 +215,12 @@ CSTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
return Success;
}
void
CSTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/,
Input /*extensionData*/)
{
}
Result
CSTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf, size_t digestBufLen)

Просмотреть файл

@ -62,6 +62,9 @@ public:
mozilla::pkix::KeyPurposeId keyPurpose) override;
virtual Result NetscapeStepUpMatchesServerAuth(
mozilla::pkix::Time notBefore, /*out*/ bool& matches) override;
virtual void NoteAuxiliaryExtension(
mozilla::pkix::AuxiliaryExtension extension,
mozilla::pkix::Input extensionData) override;
virtual Result DigestBuf(mozilla::pkix::Input item,
mozilla::pkix::DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf,

Просмотреть файл

@ -184,7 +184,7 @@ GetOCSPResponseForType(OCSPResponseType aORT, const UniqueCERTCertificate& aCert
extension.value.push_back(0x05); // tag: NULL
extension.value.push_back(0x00); // length: 0
extension.next = nullptr;
context.extensions = &extension;
context.responseExtensions = &extension;
}
if (aORT == ORTEmptyExtensions) {
context.includeEmptyExtensions = true;

Просмотреть файл

@ -105,6 +105,21 @@ enum class TrustLevel
InheritsTrust = 3 // certificate must chain to a trust anchor
};
// Extensions extracted during the verification flow.
// See TrustDomain::NoteAuxiliaryExtension.
enum class AuxiliaryExtension
{
// Certificate Transparency data, specifically Signed Certificate
// Timestamps (SCTs). See RFC 6962.
// SCT list embedded in the end entity certificate. Called by BuildCertChain
// after the certificate containing the SCTs has passed the revocation checks.
EmbeddedSCTList = 1,
// SCT list from OCSP response. Called by VerifyEncodedOCSPResponse
// when its result is a success and the SCT list is present.
SCTListFromOCSPResponse = 2
};
// CertID references the information needed to do revocation checking for the
// certificate issued by the given issuer with the given serial number.
//
@ -337,6 +352,13 @@ public:
virtual Result NetscapeStepUpMatchesServerAuth(Time notBefore,
/*out*/ bool& matches) = 0;
// Some certificate or OCSP response extensions do not directly participate
// in the verification flow, but might still be of interest to the clients
// (notably Certificate Transparency data, RFC 6962). Such extensions are
// extracted and passed to this function for further processing.
virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
Input extensionData) = 0;
// Compute a digest of the data in item using the given digest algorithm.
//
// item contains the data to hash.

Просмотреть файл

@ -244,6 +244,20 @@ PathBuildingStep::Check(Input potentialIssuerDER,
if (rv != Success) {
return RecordResult(rv, keepGoing);
}
if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
const Input* sctExtension = subject.GetSignedCertificateTimestamps();
if (sctExtension) {
Input sctList;
rv = ExtractSignedCertificateTimestampListFromExtension(*sctExtension,
sctList);
if (rv != Success) {
return RecordResult(rv, keepGoing);
}
trustDomain.NoteAuxiliaryExtension(AuxiliaryExtension::EmbeddedSCTList,
sctList);
}
}
}
return RecordResult(Success, keepGoing);

Просмотреть файл

@ -223,6 +223,11 @@ BackCert::RememberExtension(Reader& extnID, Input extnValue,
static const uint8_t id_pe_tlsfeature[] = {
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x18
};
// python DottedOIDToCode.py id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
// See Section 3.3 of RFC 6962.
static const uint8_t id_embeddedSctList[] = {
0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
};
Input* out = nullptr;
@ -269,6 +274,8 @@ BackCert::RememberExtension(Reader& extnID, Input extnValue,
out = &authorityInfoAccess;
} else if (extnID.MatchRest(id_pe_tlsfeature)) {
out = &requiredTLSFeatures;
} else if (extnID.MatchRest(id_embeddedSctList)) {
out = &signedCertificateTimestamps;
} else if (extnID.MatchRest(id_pkix_ocsp_nocheck) && critical) {
// We need to make sure we don't reject delegated OCSP response signing
// certificates that contain the id-pkix-ocsp-nocheck extension marked as
@ -300,4 +307,17 @@ BackCert::RememberExtension(Reader& extnID, Input extnValue,
return Success;
}
Result
ExtractSignedCertificateTimestampListFromExtension(Input extnValue,
Input& sctList)
{
Reader decodedValue;
Result rv = der::ExpectTagAndGetValueAtEnd(extnValue, der::OCTET_STRING,
decodedValue);
if (rv != Success) {
return rv;
}
return decodedValue.SkipToEnd(sctList);
}
} } // namespace mozilla::pkix

Просмотреть файл

@ -76,6 +76,8 @@ public:
Time* validThrough;
bool expired;
Input signedCertificateTimestamps;
// Keep track of whether the OCSP response contains the status of the
// certificate we're interested in. Responders might reply without
// including the status of any of the requested certs, we should
@ -168,6 +170,9 @@ static inline Result ResponseData(
static inline Result SingleResponse(Reader& input, Context& context);
static Result ExtensionNotUnderstood(Reader& extnID, Input extnValue,
bool critical, /*out*/ bool& understood);
static Result RememberSingleExtension(Context& context, Reader& extnID,
Input extnValue, bool critical,
/*out*/ bool& understood);
static inline Result CertID(Reader& input,
const Context& context,
/*out*/ bool& match);
@ -330,6 +335,16 @@ VerifyEncodedOCSPResponse(TrustDomain& trustDomain, const struct CertID& certID,
if (expired) {
return Result::ERROR_OCSP_OLD_RESPONSE;
}
if (context.signedCertificateTimestamps.GetLength()) {
Input sctList;
rv = ExtractSignedCertificateTimestampListFromExtension(
context.signedCertificateTimestamps, sctList);
if (rv != Success) {
return MapBadDERToMalformedOCSPResponse(rv);
}
context.trustDomain.NoteAuxiliaryExtension(
AuxiliaryExtension::SCTListFromOCSPResponse, sctList);
}
return Success;
case CertStatus::Revoked:
return Result::ERROR_REVOKED_CERTIFICATE;
@ -651,9 +666,15 @@ SingleResponse(Reader& input, Context& context)
context.expired = true;
}
rv = der::OptionalExtensions(input,
der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
ExtensionNotUnderstood);
rv = der::OptionalExtensions(
input,
der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
[&context](Reader& extnID, const Input& extnValue, bool critical,
/*out*/ bool& understood) {
return RememberSingleExtension(context, extnID, extnValue, critical,
understood);
});
if (rv != Success) {
return rv;
}
@ -826,6 +847,36 @@ ExtensionNotUnderstood(Reader& /*extnID*/, Input /*extnValue*/,
return Success;
}
Result
RememberSingleExtension(Context& context, Reader& extnID, Input extnValue,
bool /*critical*/, /*out*/ bool& understood)
{
understood = false;
// SingleExtension for Signed Certificate Timestamp List.
// See Section 3.3 of RFC 6962.
// python DottedOIDToCode.py
// id_ocsp_singleExtensionSctList 1.3.6.1.4.1.11129.2.4.5
static const uint8_t id_ocsp_singleExtensionSctList[] = {
0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x05
};
if (extnID.MatchRest(id_ocsp_singleExtensionSctList)) {
// Empty values are not allowed for this extension. Note that
// we assume this later, when checking if the extension was present.
if (extnValue.GetLength() == 0) {
return Result::ERROR_EXTENSION_VALUE_INVALID;
}
if (context.signedCertificateTimestamps.Init(extnValue) != Success) {
// Duplicate extension.
return Result::ERROR_EXTENSION_VALUE_INVALID;
}
understood = true;
}
return Success;
}
// 1. The certificate identified in a received response corresponds to
// the certificate that was identified in the corresponding request;
// 2. The signature on the response is valid;

Просмотреть файл

@ -106,6 +106,10 @@ public:
{
return MaybeInput(requiredTLSFeatures);
}
const Input* GetSignedCertificateTimestamps() const
{
return MaybeInput(signedCertificateTimestamps);
}
private:
const Input der;
@ -149,6 +153,7 @@ private:
Input subjectAltName;
Input criticalNetscapeCertificateType;
Input requiredTLSFeatures;
Input signedCertificateTimestamps; // RFC 6962 (Certificate Transparency)
Result RememberExtension(Reader& extnID, Input extnValue, bool critical,
/*out*/ bool& understood);
@ -197,6 +202,12 @@ private:
void operator=(const NonOwningDERArray&) = delete;
};
// Extracts the SignedCertificateTimestampList structure which is encoded as an
// OCTET STRING within the X.509v3 / OCSP extensions (see RFC 6962 section 3.3).
Result
ExtractSignedCertificateTimestampListFromExtension(Input extnValue,
Input& sctList);
inline unsigned int
DaysBeforeYear(unsigned int year)
{

Просмотреть файл

@ -31,11 +31,13 @@
#endif
#include <map>
#include <vector>
#if defined(_MSC_VER) && _MSC_VER < 1900
#pragma warning(pop)
#endif
#include "pkixder.h"
#include "pkixgtest.h"
using namespace mozilla::pkix;
@ -46,7 +48,8 @@ CreateCert(const char* issuerCN, // null means "empty name"
const char* subjectCN, // null means "empty name"
EndEntityOrCA endEntityOrCA,
/*optional modified*/ std::map<ByteString, ByteString>*
subjectDERToCertDER = nullptr)
subjectDERToCertDER = nullptr,
/*optional*/ const ByteString* extension = nullptr)
{
static long serialNumberValue = 0;
++serialNumberValue;
@ -56,18 +59,23 @@ CreateCert(const char* issuerCN, // null means "empty name"
ByteString issuerDER(issuerCN ? CNToDERName(issuerCN) : Name(ByteString()));
ByteString subjectDER(subjectCN ? CNToDERName(subjectCN) : Name(ByteString()));
ByteString extensions[2];
std::vector<ByteString> extensions;
if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
extensions[0] =
ByteString basicConstraints =
CreateEncodedBasicConstraints(true, nullptr, Critical::Yes);
EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
EXPECT_FALSE(ENCODING_FAILED(basicConstraints));
extensions.push_back(basicConstraints);
}
if (extension) {
extensions.push_back(*extension);
}
extensions.push_back(ByteString()); // marks the end of the list
ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
ByteString certDER(CreateEncodedCertificate(
v3, sha256WithRSAEncryption(), serialNumber, issuerDER,
oneDayBeforeNow, oneDayAfterNow, subjectDER,
*reusedKey, extensions, *reusedKey,
*reusedKey, extensions.data(), *reusedKey,
sha256WithRSAEncryption()));
EXPECT_FALSE(ENCODING_FAILED(certDER));
@ -239,15 +247,15 @@ TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength)
}
}
// A TrustDomain that explicitly fails if CheckRevocation is called.
// A TrustDomain that checks certificates against a given root certificate.
// It is initialized with the DER encoding of a root certificate that
// is treated as a trust anchor and is assumed to have issued all certificates
// (i.e. FindIssuer always attempts to build the next step in the chain with
// it).
class ExpiredCertTrustDomain final : public DefaultCryptoTrustDomain
class SingleRootTrustDomain : public DefaultCryptoTrustDomain
{
public:
explicit ExpiredCertTrustDomain(ByteString rootDER)
explicit SingleRootTrustDomain(ByteString rootDER)
: rootDER(rootDER)
{
}
@ -288,10 +296,36 @@ public:
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
return Success;
}
private:
ByteString rootDER;
};
// A TrustDomain that explicitly fails if CheckRevocation is called.
class ExpiredCertTrustDomain final : public SingleRootTrustDomain
{
public:
explicit ExpiredCertTrustDomain(ByteString rootDER)
: SingleRootTrustDomain(rootDER)
{
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
ADD_FAILURE();
return NotReached("CheckRevocation should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
};
TEST_F(pkixbuild, NoRevocationCheckingForExpiredCert)
{
const char* rootCN = "Root CA";
@ -474,3 +508,71 @@ TEST_P(pkixbuild_IssuerNameCheck, MatchingName)
INSTANTIATE_TEST_CASE_P(pkixbuild_IssuerNameCheck, pkixbuild_IssuerNameCheck,
testing::ValuesIn(ISSUER_NAME_CHECK_PARAMS));
// Records the embedded SCT list extension for later examination.
class EmbeddedSCTListTestTrustDomain final : public SingleRootTrustDomain
{
public:
explicit EmbeddedSCTListTestTrustDomain(ByteString rootDER)
: SingleRootTrustDomain(rootDER)
{
}
virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
Input extensionData) override
{
if (extension == AuxiliaryExtension::EmbeddedSCTList) {
signedCertificateTimestamps = InputToByteString(extensionData);
} else {
ADD_FAILURE();
}
}
ByteString signedCertificateTimestamps;
};
TEST_F(pkixbuild, CertificateTransparencyExtension)
{
// python security/pkix/tools/DottedOIDToCode.py --tlv
// id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
static const uint8_t tlv_id_embeddedSctList[] = {
0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
};
static const uint8_t dummySctList[] = {
0x01, 0x02, 0x03, 0x04, 0x05
};
ByteString ctExtension = TLV(der::SEQUENCE,
BytesToByteString(tlv_id_embeddedSctList) +
Boolean(false) +
TLV(der::OCTET_STRING,
// SignedCertificateTimestampList structure is encoded as an OCTET STRING
// within the X.509v3 extension (see RFC 6962 section 3.3).
// pkix decodes it internally and returns the actual structure.
TLV(der::OCTET_STRING, BytesToByteString(dummySctList))));
const char* rootCN = "Root CA";
ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA));
ASSERT_FALSE(ENCODING_FAILED(rootDER));
ByteString certDER(CreateCert(rootCN, "Cert with SCT list",
EndEntityOrCA::MustBeEndEntity,
nullptr, /*subjectDERToCertDER*/
&ctExtension));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certInput;
ASSERT_EQ(Success, certInput.Init(certDER.data(), certDER.length()));
EmbeddedSCTListTestTrustDomain extTrustDomain(rootDER);
ASSERT_EQ(Success,
BuildCertChain(extTrustDomain, certInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr /*stapledOCSPResponse*/));
ASSERT_EQ(BytesToByteString(dummySctList),
extTrustDomain.signedCertificateTimestamps);
}

Просмотреть файл

@ -178,6 +178,11 @@ public:
return NotReached("NetscapeStepUpMatchesServerAuth should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
virtual void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override
{
ADD_FAILURE();
}
};
class DefaultCryptoTrustDomain : public EverythingFailsByDefaultTrustDomain
@ -228,6 +233,10 @@ class DefaultCryptoTrustDomain : public EverythingFailsByDefaultTrustDomain
matches = true;
return Success;
}
void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override
{
}
};
class DefaultNameMatchingPolicy : public NameMatchingPolicy

Просмотреть файл

@ -22,6 +22,7 @@
* limitations under the License.
*/
#include "pkixder.h"
#include "pkixgtest.h"
using namespace mozilla::pkix;
@ -43,6 +44,19 @@ public:
trustLevel = TrustLevel::InheritsTrust;
return Success;
}
virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
Input extensionData) override
{
if (extension == AuxiliaryExtension::SCTListFromOCSPResponse) {
signedCertificateTimestamps = InputToByteString(extensionData);
} else {
// We do not currently expect to receive any other extension here.
ADD_FAILURE();
}
}
ByteString signedCertificateTimestamps;
};
namespace {
@ -199,7 +213,9 @@ public:
time_t producedAt, time_t thisUpdate,
/*optional*/ const time_t* nextUpdate,
const TestSignatureAlgorithm& signatureAlgorithm,
/*optional*/ const ByteString* certs = nullptr)
/*optional*/ const ByteString* certs = nullptr,
/*optional*/ OCSPResponseExtension* singleExtensions = nullptr,
/*optional*/ OCSPResponseExtension* responseExtensions = nullptr)
{
OCSPResponseContext context(certID, producedAt);
if (signerName) {
@ -212,6 +228,8 @@ public:
context.producedAt = producedAt;
context.signatureAlgorithm = signatureAlgorithm;
context.certs = certs;
context.singleExtensions = singleExtensions;
context.responseExtensions = responseExtensions;
context.certStatus = static_cast<uint8_t>(certStatus);
context.thisUpdate = thisUpdate;
@ -397,6 +415,46 @@ TEST_F(pkixocsp_VerifyEncodedResponse_successful, check_validThrough)
}
}
TEST_F(pkixocsp_VerifyEncodedResponse_successful, ct_extension)
{
// python DottedOIDToCode.py --tlv
// id_ocsp_singleExtensionSctList 1.3.6.1.4.1.11129.2.4.5
static const uint8_t tlv_id_ocsp_singleExtensionSctList[] = {
0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x05
};
static const uint8_t dummySctList[] = {
0x01, 0x02, 0x03, 0x04, 0x05
};
OCSPResponseExtension ctExtension;
ctExtension.id = BytesToByteString(tlv_id_ocsp_singleExtensionSctList);
// SignedCertificateTimestampList structure is encoded as an OCTET STRING
// within the extension value (see RFC 6962 section 3.3).
// pkix decodes it internally and returns the actual structure.
ctExtension.value = TLV(der::OCTET_STRING, BytesToByteString(dummySctList));
ByteString responseString(
CreateEncodedOCSPSuccessfulResponse(
OCSPResponseContext::good, *endEntityCertID, byKey,
*rootKeyPair, oneDayBeforeNow,
oneDayBeforeNow, &oneDayAfterNow,
sha256WithRSAEncryption(),
/*certs*/ nullptr,
&ctExtension));
Input response;
ASSERT_EQ(Success,
response.Init(responseString.data(), responseString.length()));
bool expired;
ASSERT_EQ(Success,
VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID,
Now(), END_ENTITY_MAX_LIFETIME_IN_DAYS,
response, expired));
ASSERT_FALSE(expired);
ASSERT_EQ(BytesToByteString(dummySctList),
trustDomain.signedCertificateTimestamps);
}
///////////////////////////////////////////////////////////////////////////////
// indirect responses (signed by a delegated OCSP responder cert)

Просмотреть файл

@ -144,12 +144,21 @@ TLV(uint8_t tag, size_t length, const ByteString& value)
return result;
}
OCSPResponseExtension::OCSPResponseExtension()
: id()
, critical(false)
, value()
, next(nullptr)
{
}
OCSPResponseContext::OCSPResponseContext(const CertID& certID, time_t time)
: certID(certID)
, responseStatus(successful)
, skipResponseBytes(false)
, producedAt(time)
, extensions(nullptr)
, singleExtensions(nullptr)
, responseExtensions(nullptr)
, includeEmptyExtensions(false)
, signatureAlgorithm(sha256WithRSAEncryption())
, badSignature(false)
@ -897,10 +906,10 @@ OCSPExtension(OCSPResponseExtension& extension)
// SEQUENCE OF Extension
// }
static ByteString
Extensions(OCSPResponseContext& context)
OCSPExtensions(OCSPResponseExtension* extensions)
{
ByteString value;
for (OCSPResponseExtension* extension = context.extensions;
for (OCSPResponseExtension* extension = extensions;
extension; extension = extension->next) {
ByteString extensionEncoded(OCSPExtension(*extension));
if (ENCODING_FAILED(extensionEncoded)) {
@ -935,8 +944,8 @@ ResponseData(OCSPResponseContext& context)
}
ByteString responses(TLV(der::SEQUENCE, response));
ByteString responseExtensions;
if (context.extensions || context.includeEmptyExtensions) {
responseExtensions = Extensions(context);
if (context.responseExtensions || context.includeEmptyExtensions) {
responseExtensions = OCSPExtensions(context.responseExtensions);
}
ByteString value;
@ -1015,12 +1024,17 @@ SingleResponse(OCSPResponseContext& context)
nextUpdateEncodedNested = TLV(der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0,
nextUpdateEncoded);
}
ByteString singleExtensions;
if (context.singleExtensions || context.includeEmptyExtensions) {
singleExtensions = OCSPExtensions(context.singleExtensions);
}
ByteString value;
value.append(certID);
value.append(certStatus);
value.append(thisUpdateEncoded);
value.append(nextUpdateEncodedNested);
value.append(singleExtensions);
return TLV(der::SEQUENCE, value);
}

Просмотреть файл

@ -376,6 +376,8 @@ ByteString CreateEncodedEKUExtension(Input eku, Critical critical);
class OCSPResponseExtension final
{
public:
OCSPResponseExtension();
ByteString id;
bool critical;
ByteString value;
@ -412,7 +414,10 @@ public:
std::time_t producedAt;
OCSPResponseExtension* extensions;
// SingleResponse extensions (for the certID given in the constructor).
OCSPResponseExtension* singleExtensions;
// ResponseData extensions.
OCSPResponseExtension* responseExtensions;
bool includeEmptyExtensions; // If true, include the extension wrapper
// regardless of if there are any actual
// extensions.