Bug 1912120 - implement updated certificate transparency policy r=jschanck

This updates the certificate transparency policy based on Chrome's policy,
found at https://googlechrome.github.io/CertificateTransparency/ct_policy.html.
Both it and the Chrome policy are similar to the Apple policy, found at
https://support.apple.com/en-us/103214.

Essentially, the policy can be satisfied in two ways, depending on the source
of the collected SCTs.
For embedded SCTs, at least one must be from a log that was Admissible
(Qualified, Usable, or ReadOnly) at the time of the check. There must be SCTs
from N distinct logs that were Admissible or Retired at the time of the check,
where N depends on the lifetime of the certificate. If the certificate lifetime
is less than or equal to 180 days, N is 2. Otherwise, N is 3. Among these SCTs,
at least two must be issued from distinct log operators.
For SCTs delivered via the TLS handshake or an OCSP response, at least two must
be from a log that was Admissible at the time of the check. Among these SCTs,
at least two must be issued from distinct log operators.

Differential Revision: https://phabricator.services.mozilla.com/D218800
This commit is contained in:
Dana Keeler 2024-08-12 19:41:58 +00:00
parent 7df2e8b66b
commit 49686e9766
13 changed files with 199 additions and 591 deletions

View File

@ -9,7 +9,6 @@
#include <stdint.h>
#include "AppTrustDomain.h"
#include "CTDiversityPolicy.h"
#include "CTKnownLogs.h"
#include "CTLogVerifier.h"
#include "ExtendedValidation.h"
@ -39,53 +38,6 @@ using namespace mozilla::psm;
mozilla::LazyLogModule gCertVerifierLog("certverifier");
// Returns the certificate validity period in calendar months (rounded down).
// "extern" to allow unit tests in CTPolicyEnforcerTest.cpp.
extern mozilla::pkix::Result GetCertLifetimeInFullMonths(Time certNotBefore,
Time certNotAfter,
size_t& months) {
if (certNotBefore >= certNotAfter) {
MOZ_ASSERT_UNREACHABLE("Expected notBefore < notAfter");
return mozilla::pkix::Result::FATAL_ERROR_INVALID_ARGS;
}
uint64_t notBeforeSeconds;
Result rv = SecondsSinceEpochFromTime(certNotBefore, &notBeforeSeconds);
if (rv != Success) {
return rv;
}
uint64_t notAfterSeconds;
rv = SecondsSinceEpochFromTime(certNotAfter, &notAfterSeconds);
if (rv != Success) {
return rv;
}
// PRTime is microseconds
PRTime notBeforePR = static_cast<PRTime>(notBeforeSeconds) * 1000000;
PRTime notAfterPR = static_cast<PRTime>(notAfterSeconds) * 1000000;
PRExplodedTime explodedNotBefore;
PRExplodedTime explodedNotAfter;
PR_ExplodeTime(notBeforePR, PR_LocalTimeParameters, &explodedNotBefore);
PR_ExplodeTime(notAfterPR, PR_LocalTimeParameters, &explodedNotAfter);
PRInt32 signedMonths =
(explodedNotAfter.tm_year - explodedNotBefore.tm_year) * 12 +
(explodedNotAfter.tm_month - explodedNotBefore.tm_month);
if (explodedNotAfter.tm_mday < explodedNotBefore.tm_mday) {
--signedMonths;
}
// Can't use `mozilla::AssertedCast<size_t>(signedMonths)` below
// since it currently generates a warning on Win x64 debug.
if (signedMonths < 0) {
MOZ_ASSERT_UNREACHABLE("Expected explodedNotBefore < explodedNotAfter");
return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
months = static_cast<size_t>(signedMonths);
return Success;
}
namespace mozilla {
namespace psm {
@ -98,7 +50,7 @@ static const unsigned int MIN_RSA_BITS_WEAK = 1024;
void CertificateTransparencyInfo::Reset() {
enabled = false;
verifyResult.Reset();
policyCompliance = CTPolicyCompliance::Unknown;
policyCompliance.reset();
}
CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
@ -268,9 +220,6 @@ void CertVerifier::LoadKnownCTLogs() {
mCTVerifier->AddLog(std::move(logVerifier));
}
// TBD: Initialize mCTDiversityPolicy with the CA dependency map
// of the known CT logs operators.
mCTDiversityPolicy = MakeUnique<CTDiversityPolicy>();
}
Result CertVerifier::VerifyCertificateTransparencyPolicy(
@ -324,7 +273,7 @@ Result CertVerifier::VerifyCertificateTransparencyPolicy(
if (ctInfo) {
CTVerifyResult emptyResult;
ctInfo->verifyResult = std::move(emptyResult);
ctInfo->policyCompliance = CTPolicyCompliance::NotEnoughScts;
ctInfo->policyCompliance.emplace(CTPolicyCompliance::NotEnoughScts);
}
return Success;
}
@ -396,30 +345,14 @@ Result CertVerifier::VerifyCertificateTransparencyPolicy(
if (rv != Success) {
return rv;
}
size_t lifetimeInMonths;
rv = GetCertLifetimeInFullMonths(notBefore, notAfter, lifetimeInMonths);
if (rv != Success) {
return rv;
}
Duration certLifetime(notBefore, notAfter);
CTLogOperatorList allOperators;
GetCTLogOperatorsFromVerifiedSCTList(result.verifiedScts, allOperators);
CTLogOperatorList dependentOperators;
rv = mCTDiversityPolicy->GetDependentOperators(builtChain, allOperators,
dependentOperators);
if (rv != Success) {
return rv;
}
CTPolicyEnforcer ctPolicyEnforcer;
CTPolicyCompliance ctPolicyCompliance;
ctPolicyEnforcer.CheckCompliance(result.verifiedScts, lifetimeInMonths,
dependentOperators, ctPolicyCompliance);
CTPolicyCompliance ctPolicyCompliance =
CheckCTPolicyCompliance(result.verifiedScts, certLifetime);
if (ctInfo) {
ctInfo->verifyResult = std::move(result);
ctInfo->policyCompliance = ctPolicyCompliance;
ctInfo->policyCompliance.emplace(ctPolicyCompliance);
}
return Success;
}

View File

@ -39,7 +39,6 @@ namespace ct {
// dependent headers and force us to export them in moz.build.
// Just forward-declare the classes here instead.
class MultiLogCTVerifier;
class CTDiversityPolicy;
} // namespace ct
} // namespace mozilla
@ -105,9 +104,7 @@ class PinningTelemetryInfo {
class CertificateTransparencyInfo {
public:
CertificateTransparencyInfo()
: enabled(false),
policyCompliance(mozilla::ct::CTPolicyCompliance::Unknown) {
CertificateTransparencyInfo() : enabled(false), policyCompliance(Nothing()) {
Reset();
}
@ -116,7 +113,7 @@ class CertificateTransparencyInfo {
// Verification result of the processed SCTs.
mozilla::ct::CTVerifyResult verifyResult;
// Connection compliance to the CT Policy.
mozilla::ct::CTPolicyCompliance policyCompliance;
Maybe<mozilla::ct::CTPolicyCompliance> policyCompliance;
void Reset();
};
@ -266,7 +263,6 @@ class CertVerifier {
// We only have a forward declarations of these classes (see above)
// so we must allocate dynamically.
UniquePtr<mozilla::ct::MultiLogCTVerifier> mCTVerifier;
UniquePtr<mozilla::ct::CTDiversityPolicy> mCTDiversityPolicy;
void LoadKnownCTLogs();
mozilla::pkix::Result VerifyCertificateTransparencyPolicy(

View File

@ -1,39 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CTDiversityPolicy.h"
namespace mozilla {
namespace ct {
typedef pkix::Result Result;
void GetCTLogOperatorsFromVerifiedSCTList(const VerifiedSCTList& list,
CTLogOperatorList& operators) {
operators.clear();
for (const VerifiedSCT& verifiedSct : list) {
CTLogOperatorId sctLogOperatorId = verifiedSct.logOperatorId;
bool alreadyAdded = false;
for (CTLogOperatorId id : operators) {
if (id == sctLogOperatorId) {
alreadyAdded = true;
break;
}
}
if (!alreadyAdded) {
operators.push_back(sctLogOperatorId);
}
}
}
Result CTDiversityPolicy::GetDependentOperators(
const nsTArray<nsTArray<uint8_t>>& builtChain,
const CTLogOperatorList& operators, CTLogOperatorList& dependentOperators) {
return pkix::Success;
}
} // namespace ct
} // namespace mozilla

View File

@ -1,43 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CTDiversityPolicy_h
#define CTDiversityPolicy_h
#include "CTLog.h"
#include "CTVerifyResult.h"
#include "certt.h"
#include "nsTArray.h"
#include "mozpkix/Result.h"
namespace mozilla {
namespace ct {
// Retuns the list of unique CT log operator IDs appearing in the provided
// list of verified SCTs.
void GetCTLogOperatorsFromVerifiedSCTList(const VerifiedSCTList& list,
CTLogOperatorList& operators);
// Helper class used by CTPolicyEnforcer to check the CT log operators
// diversity requirements of the CT Policy.
// See CTPolicyEnforcer.h for more details.
class CTDiversityPolicy {
public:
// Given a certificate chain and a set of CT log operators,
// returns the subset of log operators that are dependent on the CA
// issuing the certificate (as defined by the CT Policy).
//
// NOTE: TBD, PENDING FINALIZATION OF MOZILLA CT POLICY.
pkix::Result GetDependentOperators(
const nsTArray<nsTArray<uint8_t>>& builtChain,
const CTLogOperatorList& operators,
CTLogOperatorList& dependentOperators);
};
} // namespace ct
} // namespace mozilla
#endif // CTDiversityPolicy_h

View File

@ -7,115 +7,24 @@
#include "CTPolicyEnforcer.h"
#include "mozilla/Assertions.h"
#include <algorithm>
#include "mozpkix/Time.h"
#include <set>
#include <stdint.h>
#include <vector>
namespace mozilla {
namespace ct {
using namespace mozilla::pkix;
// Returns the number of embedded SCTs required to be present on a
// certificate for Qualification Case #2 (embedded SCTs).
static size_t GetRequiredEmbeddedSctsCount(
size_t certLifetimeInFullCalendarMonths) {
// "there are Embedded SCTs from AT LEAST N+1 once or currently qualified
// logs, where N is the lifetime of the certificate in years (normally
// rounding up, but rounding down when up to 3 months over), and must be
// at least 1"
return 1 + (certLifetimeInFullCalendarMonths + 9) / 12;
}
// Whether a valid embedded SCT is present in the list.
static bool HasValidEmbeddedSct(const VerifiedSCTList& verifiedScts) {
for (const VerifiedSCT& verifiedSct : verifiedScts) {
if (verifiedSct.logState == CTLogState::Admissible &&
verifiedSct.origin == SCTOrigin::Embedded) {
return true;
}
}
return false;
}
// Whether a valid non-embedded SCT is present in the list.
static bool HasValidNonEmbeddedSct(const VerifiedSCTList& verifiedScts) {
for (const VerifiedSCT& verifiedSct : verifiedScts) {
if (verifiedSct.logState == CTLogState::Admissible &&
(verifiedSct.origin == SCTOrigin::TLSExtension ||
verifiedSct.origin == SCTOrigin::OCSPResponse)) {
return true;
}
}
return false;
}
// Given a list of verified SCTs, counts the number of distinct CA-independent
// log operators running the CT logs that issued the SCTs which satisfy
// the provided boolean predicate.
template <typename SelectFunc>
void CountIndependentLogOperatorsForSelectedScts(
const VerifiedSCTList& verifiedScts,
const CTLogOperatorList& dependentOperators, size_t& count,
SelectFunc selected) {
CTLogOperatorList operatorIds;
for (const VerifiedSCT& verifiedSct : verifiedScts) {
CTLogOperatorId sctLogOperatorId = verifiedSct.logOperatorId;
// Check if |sctLogOperatorId| is CA-dependent.
bool isDependentOperator = false;
for (CTLogOperatorId dependentOperator : dependentOperators) {
if (sctLogOperatorId == dependentOperator) {
isDependentOperator = true;
break;
}
}
if (isDependentOperator || !selected(verifiedSct)) {
continue;
}
// Check if |sctLogOperatorId| is in |operatorIds|...
bool alreadyAdded = false;
for (CTLogOperatorId id : operatorIds) {
if (id == sctLogOperatorId) {
alreadyAdded = true;
break;
}
}
// ...and if not, add it.
if (!alreadyAdded) {
operatorIds.push_back(sctLogOperatorId);
}
}
count = operatorIds.size();
}
// Given a list of verified SCTs, counts the number of distinct CT logs
// that issued the SCTs that satisfy the |selected| predicate.
template <typename SelectFunc>
static void CountLogsForSelectedScts(const VerifiedSCTList& verifiedScts,
size_t& count, SelectFunc selected) {
// Keep pointers to log ids (of type Buffer) from |verifiedScts| to save on
// memory allocations.
std::vector<const Buffer*> logIds;
for (const VerifiedSCT& verifiedSct : verifiedScts) {
if (!selected(verifiedSct)) {
continue;
}
const Buffer* sctLogId = &verifiedSct.sct.logId;
// Check if |sctLogId| points to data already in |logIds|...
bool alreadyAdded = false;
for (const Buffer* logId : logIds) {
if (*logId == *sctLogId) {
alreadyAdded = true;
break;
}
}
// ...and if not, add it.
if (!alreadyAdded) {
logIds.push_back(sctLogId);
}
}
count = logIds.size();
// Returns the number of embedded SCTs required to be present in a certificate.
// For certificates with a lifetime of less than or equal to 180 days, only 2
// embedded SCTs are required. Otherwise 3 are required.
const Duration ONE_HUNDRED_AND_EIGHTY_DAYS =
Duration(180 * Time::ONE_DAY_IN_SECONDS);
size_t GetRequiredEmbeddedSctsCount(Duration certLifetime) {
// pkix::Duration doesn't define operator<=, hence phrasing this comparison
// in an awkward way
return ONE_HUNDRED_AND_EIGHTY_DAYS < certLifetime ? 3 : 2;
}
// Calculates the effective issuance time of connection's certificate using
@ -130,8 +39,7 @@ static void CountLogsForSelectedScts(const VerifiedSCTList& verifiedScts,
// delivered via OCSP/TLS extension will cover the full certificate,
// which necessarily will exist only after the precertificate
// has been logged and the actual certificate issued.
static uint64_t GetEffectiveCertIssuanceTime(
const VerifiedSCTList& verifiedScts) {
uint64_t GetEffectiveCertIssuanceTime(const VerifiedSCTList& verifiedScts) {
uint64_t result = UINT64_MAX;
for (const VerifiedSCT& verifiedSct : verifiedScts) {
if (verifiedSct.logState == CTLogState::Admissible) {
@ -144,8 +52,8 @@ static uint64_t GetEffectiveCertIssuanceTime(
// Checks if the log that issued the given SCT is "once or currently qualified"
// (i.e. was qualified at the time of the certificate issuance). In addition,
// makes sure the SCT is before the retirement timestamp.
static bool LogWasQualifiedForSct(const VerifiedSCT& verifiedSct,
uint64_t certIssuanceTime) {
bool LogWasQualifiedForSct(const VerifiedSCT& verifiedSct,
uint64_t certIssuanceTime) {
switch (verifiedSct.logState) {
case CTLogState::Admissible:
return true;
@ -159,121 +67,91 @@ static bool LogWasQualifiedForSct(const VerifiedSCT& verifiedSct,
return false;
}
// "A certificate is CT Qualified if it is presented with at least two SCTs
// from once or currently qualified logs run by a minimum of two entities
// independent of the CA and of each other."
// By the preexisting certificate exception provision (not currently
// implemented), certificates "are CT Qualified if they are presented with SCTs
// from once or currently qualified logs run by a minimum of one entity
// independent of the CA."
static void CheckOperatorDiversityCompliance(
const VerifiedSCTList& verifiedScts, uint64_t certIssuanceTime,
const CTLogOperatorList& dependentOperators, bool& compliant) {
size_t independentOperatorsCount;
CountIndependentLogOperatorsForSelectedScts(
verifiedScts, dependentOperators, independentOperatorsCount,
[certIssuanceTime](const VerifiedSCT& verifiedSct) -> bool {
return LogWasQualifiedForSct(verifiedSct, certIssuanceTime);
});
// Having at least 2 operators implies we have at least 2 SCTs.
// For the preexisting certificate exception provision (1 operator) we will
// need to include an additional SCTs count check using
// CountLogsForSelectedScts(verifiedScts, sctsCount,
// [certIssuanceTime](const VerifiedSCT& verifiedSct)->bool {
// return LogWasQualifiedForSct(verifiedSct, certIssuanceTime);
// });
compliant = independentOperatorsCount >= 2;
}
// Qualification Case #1 (non-embedded SCTs) - the following must hold:
// a. An SCT from a log qualified at the time of check is presented via the
// TLS extension OR is embedded within a stapled OCSP response;
// AND
// b. There are at least two SCTs from logs qualified at the time of check,
// presented via any method.
static void CheckNonEmbeddedCompliance(const VerifiedSCTList& verifiedScts,
bool& compliant) {
if (!HasValidNonEmbeddedSct(verifiedScts)) {
compliant = false;
return;
// Qualification for embedded SCTs:
// There must be at least one embedded SCT from a log that was Admissible (i.e.
// Qualified, Usable, or ReadOnly) at the time of the check.
// There must be at least N embedded SCTs from distinct logs that were
// Admissible or Retired at the time of the check, where N depends on the
// lifetime of the certificate. If the certificate lifetime is less than or
// equal to 180 days, N is 2. Otherwise, N is 3.
// Among these SCTs, at least two must be issued from distinct log operators.
CTPolicyCompliance EmbeddedSCTsCompliant(const VerifiedSCTList& verifiedScts,
uint64_t certIssuanceTime,
Duration certLifetime) {
size_t admissibleCount = 0;
size_t admissibleOrRetiredCount = 0;
std::set<CTLogOperatorId> logOperators;
std::set<Buffer> logIds;
for (const auto& verifiedSct : verifiedScts) {
if (verifiedSct.origin != SCTOrigin::Embedded) {
continue;
}
if (verifiedSct.logState != CTLogState::Admissible &&
!LogWasQualifiedForSct(verifiedSct, certIssuanceTime)) {
continue;
}
// Note that a single SCT can count for both the "from a log that was
// admissible" case and the "from a log that was admissible or retired"
// case.
if (verifiedSct.logState == CTLogState::Admissible) {
admissibleCount++;
}
if (LogWasQualifiedForSct(verifiedSct, certIssuanceTime)) {
admissibleOrRetiredCount++;
logIds.insert(verifiedSct.sct.logId);
}
logOperators.insert(verifiedSct.logOperatorId);
}
size_t validSctsCount;
CountLogsForSelectedScts(
verifiedScts, validSctsCount, [](const VerifiedSCT& verifiedSct) -> bool {
return verifiedSct.logState == CTLogState::Admissible;
});
compliant = validSctsCount >= 2;
size_t requiredEmbeddedScts = GetRequiredEmbeddedSctsCount(certLifetime);
if (admissibleCount < 1 || admissibleOrRetiredCount < requiredEmbeddedScts) {
return CTPolicyCompliance::NotEnoughScts;
}
if (logIds.size() < requiredEmbeddedScts || logOperators.size() < 2) {
return CTPolicyCompliance::NotDiverseScts;
}
return CTPolicyCompliance::Compliant;
}
// Qualification Case #2 (embedded SCTs) - the following must hold:
// a. An Embedded SCT from a log qualified at the time of check is presented;
// AND
// b. There are Embedded SCTs from AT LEAST N + 1 once or currently qualified
// logs, where N is the lifetime of the certificate in years (normally
// rounding up, but rounding down when up to 3 months over), and must be
// at least 1.
static void CheckEmbeddedCompliance(const VerifiedSCTList& verifiedScts,
size_t certLifetimeInCalendarMonths,
uint64_t certIssuanceTime,
bool& compliant) {
if (!HasValidEmbeddedSct(verifiedScts)) {
compliant = false;
return;
// Qualification for non-embedded SCTs (i.e. SCTs delivered via TLS handshake
// or OCSP response):
// There must be at least two SCTs from logs that were Admissible (i.e.
// Qualified, Usable, or ReadOnly) at the time of the check. Among these SCTs,
// at least two must be issued from distinct log operators.
CTPolicyCompliance NonEmbeddedSCTsCompliant(
const VerifiedSCTList& verifiedScts) {
size_t admissibleCount = 0;
std::set<CTLogOperatorId> logOperators;
std::set<Buffer> logIds;
for (const auto& verifiedSct : verifiedScts) {
if (verifiedSct.origin == SCTOrigin::Embedded) {
continue;
}
if (verifiedSct.logState != CTLogState::Admissible) {
continue;
}
admissibleCount++;
logIds.insert(verifiedSct.sct.logId);
logOperators.insert(verifiedSct.logOperatorId);
}
// Count the compliant embedded SCTs. Only a single SCT from each log
// is accepted. Note that a given log might return several different SCTs
// for the same precertificate (it is permitted, but advised against).
size_t embeddedSctsCount;
CountLogsForSelectedScts(
verifiedScts, embeddedSctsCount,
[certIssuanceTime](const VerifiedSCT& verifiedSct) -> bool {
return verifiedSct.origin == SCTOrigin::Embedded &&
LogWasQualifiedForSct(verifiedSct, certIssuanceTime);
});
size_t requiredSctsCount =
GetRequiredEmbeddedSctsCount(certLifetimeInCalendarMonths);
compliant = embeddedSctsCount >= requiredSctsCount;
if (admissibleCount < 2) {
return CTPolicyCompliance::NotEnoughScts;
}
if (logIds.size() < 2 || logOperators.size() < 2) {
return CTPolicyCompliance::NotDiverseScts;
}
return CTPolicyCompliance::Compliant;
}
void CTPolicyEnforcer::CheckCompliance(
const VerifiedSCTList& verifiedScts, size_t certLifetimeInCalendarMonths,
const CTLogOperatorList& dependentOperators,
CTPolicyCompliance& compliance) {
CTPolicyCompliance CheckCTPolicyCompliance(const VerifiedSCTList& verifiedScts,
Duration certLifetime) {
if (NonEmbeddedSCTsCompliant(verifiedScts) == CTPolicyCompliance::Compliant) {
return CTPolicyCompliance::Compliant;
}
uint64_t certIssuanceTime = GetEffectiveCertIssuanceTime(verifiedScts);
bool diversityOK;
CheckOperatorDiversityCompliance(verifiedScts, certIssuanceTime,
dependentOperators, diversityOK);
bool nonEmbeddedCaseOK;
CheckNonEmbeddedCompliance(verifiedScts, nonEmbeddedCaseOK);
bool embeddedCaseOK;
CheckEmbeddedCompliance(verifiedScts, certLifetimeInCalendarMonths,
certIssuanceTime, embeddedCaseOK);
if (nonEmbeddedCaseOK || embeddedCaseOK) {
compliance = diversityOK ? CTPolicyCompliance::Compliant
: CTPolicyCompliance::NotDiverseScts;
} else {
// Not enough SCTs are present to satisfy either case of the policy.
compliance = CTPolicyCompliance::NotEnoughScts;
}
switch (compliance) {
case CTPolicyCompliance::Compliant:
case CTPolicyCompliance::NotEnoughScts:
case CTPolicyCompliance::NotDiverseScts:
break;
case CTPolicyCompliance::Unknown:
default:
assert(false);
}
return EmbeddedSCTsCompliant(verifiedScts, certIssuanceTime, certLifetime);
}
} // namespace ct

View File

@ -10,15 +10,14 @@
#include "CTLog.h"
#include "CTVerifyResult.h"
#include "mozpkix/Result.h"
#include "mozpkix/Time.h"
namespace mozilla {
namespace ct {
// Information about the compliance of the TLS connection with the
// Certificate Transparency policy.
// A helper enum to describe the result of running CheckCTPolicyCompliance on a
// collection of verified SCTs.
enum class CTPolicyCompliance {
// Compliance not checked or not applicable.
Unknown,
// The connection complied with the certificate policy
// by including SCTs that satisfy the policy.
Compliant,
@ -30,34 +29,30 @@ enum class CTPolicyCompliance {
NotDiverseScts,
};
// Checks whether a TLS connection complies with the current CT policy.
// The implemented policy is based on the Mozilla CT Policy draft 0.1.0
// (see https://docs.google.com/document/d/
// 1rnqYYwscAx8WhS-MCdTiNzYQus9e37HuVyafQvEeNro/edit).
// Checks the collected verified SCTs for compliance with the CT policy.
// The policy is based on Chrome's policy as described here:
// https://googlechrome.github.io/CertificateTransparency/ct_policy.html
// This policy (as well as Chrome's), is very similar to Apple's:
// https://support.apple.com/en-us/103214
// Essentially, the policy can be satisfied in two ways, depending on the
// source of the collected SCTs.
// For embedded SCTs, at least one must be from a log that was Admissible
// (Qualified, Usable, or ReadOnly) at the time of the check. There must be
// SCTs from N distinct logs that were Admissible or Retired at the time of the
// check, where N depends on the lifetime of the certificate. If the
// certificate lifetime is less than or equal to 180 days, N is 2. Otherwise, N
// is 3. Among these SCTs, at least two must be issued from distinct log
// operators.
// For SCTs delivered via the TLS handshake or an OCSP response, at least two
// must be from a log that was Admissible at the time of the check. Among these
// SCTs, at least two must be issued from distinct log operators.
//
// NOTE: CT DIVERSITY REQUIREMENT IS TBD, PENDING FINALIZATION
// OF MOZILLA CT POLICY. Specifically:
// 1. CT log operators being CA-dependent is not currently taken into account
// (see CTDiversityPolicy.h).
// 2. The preexisting certificate exception provision of the operator diversity
// requirement is not implemented (see "CT Qualified" section of the policy and
// CheckOperatorDiversityCompliance in CTPolicyEnforcer.cpp).
class CTPolicyEnforcer {
public:
// |verifiedSct| - SCTs present on the connection along with their
// verification status.
// |certLifetimeInCalendarMonths| - certificate lifetime in full calendar
// months (i.e. rounded down), based on the notBefore/notAfter fields.
// |dependentOperators| - which CT log operators are dependent on the CA
// that issued the certificate. SCTs issued by logs associated with such
// operators are treated differenly when evaluating the policy.
// See CTDiversityPolicy class.
// |compliance| - the result of the compliance check.
void CheckCompliance(const VerifiedSCTList& verifiedScts,
size_t certLifetimeInCalendarMonths,
const CTLogOperatorList& dependentOperators,
CTPolicyCompliance& compliance);
};
// |verifiedSct| - SCTs present on the connection along with their verification
// status.
// |certLifetime| - certificate lifetime, based on the notBefore/notAfter
// fields.
CTPolicyCompliance CheckCTPolicyCompliance(const VerifiedSCTList& verifiedScts,
pkix::Duration certLifetime);
} // namespace ct
} // namespace mozilla

View File

@ -21,7 +21,6 @@ EXPORTS += [
UNIFIED_SOURCES += [
"BTVerifier.cpp",
"Buffer.cpp",
"CTDiversityPolicy.cpp",
"CTLogVerifier.cpp",
"CTObjectsExtractor.cpp",
"CTPolicyEnforcer.cpp",

View File

@ -1,15 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CTDiversityPolicy.h"
namespace mozilla {
namespace ct {
// TBD, PENDING FINALIZATION OF MOZILLA CT POLICY.
}
} // namespace mozilla

View File

@ -18,11 +18,6 @@
#include "hasht.h"
#include "prtime.h"
// Implemented in CertVerifier.cpp.
extern mozilla::pkix::Result GetCertLifetimeInFullMonths(
mozilla::pkix::Time certNotBefore, mozilla::pkix::Time certNotAfter,
size_t& months);
namespace mozilla {
namespace ct {
@ -30,11 +25,6 @@ using namespace mozilla::pkix;
class CTPolicyEnforcerTest : public ::testing::Test {
public:
void SetUp() override {
OPERATORS_1_AND_2.push_back(OPERATOR_1);
OPERATORS_1_AND_2.push_back(OPERATOR_2);
}
void GetLogId(Buffer& logId, size_t logNo) {
logId.resize(SHA256_LENGTH);
std::fill(logId.begin(), logId.end(), 0);
@ -68,37 +58,28 @@ class CTPolicyEnforcerTest : public ::testing::Test {
}
void CheckCompliance(const VerifiedSCTList& verifiedSct,
size_t certLifetimeInCalendarMonths,
const CTLogOperatorList& dependentLogOperators,
Duration certLifetime,
CTPolicyCompliance expectedCompliance) {
CTPolicyCompliance compliance;
mPolicyEnforcer.CheckCompliance(verifiedSct, certLifetimeInCalendarMonths,
dependentLogOperators, compliance);
CTPolicyCompliance compliance =
CheckCTPolicyCompliance(verifiedSct, certLifetime);
EXPECT_EQ(expectedCompliance, compliance);
}
protected:
CTPolicyEnforcer mPolicyEnforcer;
const size_t LOG_1 = 1;
const size_t LOG_2 = 2;
const size_t LOG_3 = 3;
const size_t LOG_4 = 4;
const size_t LOG_5 = 5;
const CTLogOperatorId OPERATOR_1 = 1;
const CTLogOperatorId OPERATOR_2 = 2;
const CTLogOperatorId OPERATOR_3 = 3;
CTLogOperatorList NO_OPERATORS;
CTLogOperatorList OPERATORS_1_AND_2;
const SCTOrigin ORIGIN_EMBEDDED = SCTOrigin::Embedded;
const SCTOrigin ORIGIN_TLS = SCTOrigin::TLSExtension;
const SCTOrigin ORIGIN_OCSP = SCTOrigin::OCSPResponse;
// 4 years of cert lifetime requires 5 SCTs for the embedded case.
const size_t DEFAULT_MONTHS = 4 * 12L;
// 1 year of cert lifetime requires 3 SCTs for the embedded case.
const Duration DEFAULT_LIFETIME = Duration(365 * Time::ONE_DAY_IN_SECONDS);
// Date.parse("2015-08-15T00:00:00Z")
const uint64_t TIMESTAMP_1 = 1439596800000L;
@ -110,7 +91,7 @@ class CTPolicyEnforcerTest : public ::testing::Test {
const uint64_t BEFORE_RETIREMENT = 1459468800000L;
// Date.parse("2016-04-16T00:00:00Z")
const uint64_t AFTER_DISQUALIFIED = 1460764800000L;
const uint64_t AFTER_RETIREMENT = 1460764800000L;
};
TEST_F(CTPolicyEnforcerTest, ConformsToCTPolicyWithNonEmbeddedSCTs) {
@ -119,46 +100,42 @@ TEST_F(CTPolicyEnforcerTest, ConformsToCTPolicyWithNonEmbeddedSCTs) {
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_TLS, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_TLS, TIMESTAMP_1);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::Compliant);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::Compliant);
}
TEST_F(CTPolicyEnforcerTest, DoesNotConformNotEnoughDiverseNonEmbeddedSCTs) {
VerifiedSCTList scts;
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_TLS, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_TLS, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_1, ORIGIN_TLS, TIMESTAMP_1);
CheckCompliance(scts, DEFAULT_MONTHS, OPERATORS_1_AND_2,
CTPolicyCompliance::NotDiverseScts);
// The implementation attempts to fulfill the non-embedded compliance case
// first. Because the non-embedded SCTs do not have enough log diversity, the
// implementation then attempts to fulfill the embedded compliance case.
// Because there are no embedded SCTs, it returns a "not enough SCTs" error.
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::NotEnoughScts);
}
TEST_F(CTPolicyEnforcerTest, ConformsToCTPolicyWithEmbeddedSCTs) {
VerifiedSCTList scts;
// 5 embedded SCTs required for DEFAULT_MONTHS.
// 3 embedded SCTs required for DEFAULT_LIFETIME.
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_3, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_4, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_5, OPERATOR_2, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_3, OPERATOR_2, ORIGIN_EMBEDDED, TIMESTAMP_1);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::Compliant);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::Compliant);
}
TEST_F(CTPolicyEnforcerTest, DoesNotConformNotEnoughDiverseEmbeddedSCTs) {
VerifiedSCTList scts;
// 5 embedded SCTs required for DEFAULT_MONTHS.
// 3 embedded SCTs required for DEFAULT_LIFETIME.
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_3, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_4, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_5, OPERATOR_2, ORIGIN_EMBEDDED, TIMESTAMP_1);
CheckCompliance(scts, DEFAULT_MONTHS, OPERATORS_1_AND_2,
CTPolicyCompliance::NotDiverseScts);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::NotDiverseScts);
}
TEST_F(CTPolicyEnforcerTest, ConformsToCTPolicyWithPooledNonEmbeddedSCTs) {
@ -167,18 +144,16 @@ TEST_F(CTPolicyEnforcerTest, ConformsToCTPolicyWithPooledNonEmbeddedSCTs) {
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_OCSP, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_TLS, TIMESTAMP_1);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::Compliant);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::Compliant);
}
TEST_F(CTPolicyEnforcerTest, ConformsToCTPolicyWithPooledEmbeddedSCTs) {
TEST_F(CTPolicyEnforcerTest, DoesNotConformToCTPolicyWithPooledEmbeddedSCTs) {
VerifiedSCTList scts;
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_OCSP, TIMESTAMP_1);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::Compliant);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::NotEnoughScts);
}
TEST_F(CTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughSCTs) {
@ -187,8 +162,7 @@ TEST_F(CTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughSCTs) {
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_EMBEDDED, TIMESTAMP_1);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::NotEnoughScts);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::NotEnoughScts);
}
TEST_F(CTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughFreshSCTs) {
@ -202,113 +176,98 @@ TEST_F(CTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughFreshSCTs) {
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_TLS, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_TLS, BEFORE_RETIREMENT,
CTLogState::Retired);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::NotEnoughScts);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::NotEnoughScts);
// SCT from after disqualification.
scts.clear();
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_TLS, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_TLS, AFTER_DISQUALIFIED,
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_TLS, AFTER_RETIREMENT,
CTLogState::Retired);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::NotEnoughScts);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::NotEnoughScts);
// Embedded SCT from before disqualification.
scts.clear();
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_TLS, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_EMBEDDED, BEFORE_RETIREMENT,
CTLogState::Retired);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::NotEnoughScts);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::NotEnoughScts);
// Embedded SCT from after disqualification.
scts.clear();
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_TLS, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_EMBEDDED, AFTER_DISQUALIFIED,
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_EMBEDDED, AFTER_RETIREMENT,
CTLogState::Retired);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::NotEnoughScts);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::NotEnoughScts);
}
TEST_F(CTPolicyEnforcerTest, ConformsWithRetiredLogBeforeDisqualificationDate) {
VerifiedSCTList scts;
// 3 embedded SCTs required for DEFAULT_LIFETIME.
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_3, OPERATOR_2, ORIGIN_EMBEDDED, BEFORE_RETIREMENT,
CTLogState::Retired);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::Compliant);
}
TEST_F(CTPolicyEnforcerTest,
ConformsWithDisqualifiedLogBeforeDisqualificationDate) {
DoesNotConformWithRetiredLogAfterDisqualificationDate) {
VerifiedSCTList scts;
// 5 embedded SCTs required for DEFAULT_MONTHS.
// 3 embedded SCTs required for DEFAULT_LIFETIME.
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_3, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_4, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_5, OPERATOR_2, ORIGIN_EMBEDDED, BEFORE_RETIREMENT,
AddSct(scts, LOG_3, OPERATOR_2, ORIGIN_EMBEDDED, AFTER_RETIREMENT,
CTLogState::Retired);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::Compliant);
}
TEST_F(CTPolicyEnforcerTest,
DoesNotConformWithDisqualifiedLogAfterDisqualificationDate) {
VerifiedSCTList scts;
// 5 embedded SCTs required for DEFAULT_MONTHS.
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_2, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_3, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_4, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_5, OPERATOR_2, ORIGIN_EMBEDDED, AFTER_DISQUALIFIED,
CTLogState::Retired);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::NotEnoughScts);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::NotEnoughScts);
}
TEST_F(CTPolicyEnforcerTest,
DoesNotConformWithIssuanceDateAfterDisqualificationDate) {
VerifiedSCTList scts;
// 5 embedded SCTs required for DEFAULT_MONTHS.
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_EMBEDDED, AFTER_DISQUALIFIED,
// 3 embedded SCTs required for DEFAULT_LIFETIME.
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_EMBEDDED, AFTER_RETIREMENT,
CTLogState::Retired);
AddSct(scts, LOG_2, OPERATOR_1, ORIGIN_EMBEDDED, AFTER_DISQUALIFIED);
AddSct(scts, LOG_3, OPERATOR_1, ORIGIN_EMBEDDED, AFTER_DISQUALIFIED);
AddSct(scts, LOG_4, OPERATOR_1, ORIGIN_EMBEDDED, AFTER_DISQUALIFIED);
AddSct(scts, LOG_5, OPERATOR_2, ORIGIN_EMBEDDED, AFTER_DISQUALIFIED);
AddSct(scts, LOG_2, OPERATOR_1, ORIGIN_EMBEDDED, AFTER_RETIREMENT);
AddSct(scts, LOG_3, OPERATOR_2, ORIGIN_EMBEDDED, AFTER_RETIREMENT);
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::NotEnoughScts);
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::NotEnoughScts);
}
TEST_F(CTPolicyEnforcerTest,
DoesNotConformToCTPolicyNotEnoughUniqueEmbeddedLogs) {
DoesNotConformToCTPolicyNotEnoughUniqueEmbeddedRetiredLogs) {
VerifiedSCTList scts;
// Operator #1
AddSct(scts, LOG_1, OPERATOR_1, ORIGIN_EMBEDDED, TIMESTAMP_1);
// Operator #2, different logs
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_3, OPERATOR_2, ORIGIN_EMBEDDED, TIMESTAMP_1);
// Operator #3, same log
AddSct(scts, LOG_4, OPERATOR_3, ORIGIN_EMBEDDED, TIMESTAMP_1);
AddSct(scts, LOG_4, OPERATOR_3, ORIGIN_EMBEDDED, TIMESTAMP_1);
// Operator #2, same retired logs
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_EMBEDDED, BEFORE_RETIREMENT,
CTLogState::Retired);
AddSct(scts, LOG_2, OPERATOR_2, ORIGIN_EMBEDDED, BEFORE_RETIREMENT,
CTLogState::Retired);
// 5 embedded SCTs required. However, only 4 are from distinct logs.
CheckCompliance(scts, DEFAULT_MONTHS, NO_OPERATORS,
CTPolicyCompliance::NotEnoughScts);
// 3 embedded SCTs required. However, only 2 are from distinct logs.
CheckCompliance(scts, DEFAULT_LIFETIME, CTPolicyCompliance::NotDiverseScts);
}
TEST_F(CTPolicyEnforcerTest,
ConformsToPolicyExactNumberOfSCTsForValidityPeriod) {
// Test multiple validity periods.
const struct TestData {
size_t certLifetimeInCalendarMonths;
Duration certLifetime;
size_t sctsRequired;
} kTestData[] = {{3, 2}, {12 + 2, 2}, {12 + 3, 3},
{2 * 12 + 2, 3}, {2 * 12 + 3, 4}, {3 * 12 + 2, 4},
{3 * 12 + 4, 5}};
} kTestData[] = {{Duration(90 * Time::ONE_DAY_IN_SECONDS), 2},
{Duration(180 * Time::ONE_DAY_IN_SECONDS), 2},
{Duration(181 * Time::ONE_DAY_IN_SECONDS), 3},
{Duration(365 * Time::ONE_DAY_IN_SECONDS), 3}};
for (size_t i = 0; i < MOZILLA_CT_ARRAY_LENGTH(kTestData); ++i) {
SCOPED_TRACE(i);
size_t months = kTestData[i].certLifetimeInCalendarMonths;
Duration certLifetime = kTestData[i].certLifetime;
size_t sctsRequired = kTestData[i].sctsRequired;
// Less SCTs than required is not enough.
@ -317,8 +276,8 @@ TEST_F(CTPolicyEnforcerTest,
VerifiedSCTList scts;
AddMultipleScts(scts, sctsAvailable, 1, ORIGIN_EMBEDDED, TIMESTAMP_1);
CTPolicyCompliance compliance;
mPolicyEnforcer.CheckCompliance(scts, months, NO_OPERATORS, compliance);
CTPolicyCompliance compliance =
CheckCTPolicyCompliance(scts, certLifetime);
EXPECT_EQ(CTPolicyCompliance::NotEnoughScts, compliance)
<< "i=" << i << " sctsRequired=" << sctsRequired
<< " sctsAvailable=" << sctsAvailable;
@ -328,54 +287,10 @@ TEST_F(CTPolicyEnforcerTest,
VerifiedSCTList scts;
AddMultipleScts(scts, sctsRequired, 2, ORIGIN_EMBEDDED, TIMESTAMP_1);
CTPolicyCompliance compliance;
mPolicyEnforcer.CheckCompliance(scts, months, NO_OPERATORS, compliance);
CTPolicyCompliance compliance = CheckCTPolicyCompliance(scts, certLifetime);
EXPECT_EQ(CTPolicyCompliance::Compliant, compliance) << "i=" << i;
}
}
TEST_F(CTPolicyEnforcerTest, TestEdgeCasesOfGetCertLifetimeInFullMonths) {
const struct TestData {
uint64_t notBefore;
uint64_t notAfter;
size_t expectedMonths;
} kTestData[] = {
{ // 1 second less than 1 month
1424863500000000, // Date.parse("2015-02-25T11:25:00Z") * 1000
1427196299000000, // Date.parse("2015-03-24T11:24:59Z") * 1000
0},
{ // exactly 1 month
1424863500000000, // Date.parse("2015-02-25T11:25:00Z") * 1000
1427282700000000, // Date.parse("2015-03-25T11:25:00Z") * 1000
1},
{ // 1 year, 1 month
1427282700000000, // Date.parse("2015-03-25T11:25:00Z") * 1000
1461583500000000, // Date.parse("2016-04-25T11:25:00Z") * 1000
13},
{// 1 year, 1 month, first day of notBefore month, last of notAfter
1425209100000000, // Date.parse("2015-03-01T11:25:00Z") * 1000
1462015500000000, // Date.parse("2016-04-30T11:25:00Z") * 1000
13},
{// 1 year, adjacent months, last day of notBefore month, first of
// notAfter
1427801100000000, // Date.parse("2015-03-31T11:25:00Z") * 1000
1459509900000000, // Date.parse("2016-04-01T11:25:00Z") * 1000
12}};
for (size_t i = 0; i < MOZILLA_CT_ARRAY_LENGTH(kTestData); ++i) {
SCOPED_TRACE(i);
size_t months;
ASSERT_EQ(Success,
GetCertLifetimeInFullMonths(mozilla::pkix::TimeFromEpochInSeconds(
kTestData[i].notBefore / 1000000),
mozilla::pkix::TimeFromEpochInSeconds(
kTestData[i].notAfter / 1000000),
months))
<< "i=" << i;
EXPECT_EQ(kTestData[i].expectedMonths, months) << "i=" << i;
}
}
} // namespace ct
} // namespace mozilla

View File

@ -37,7 +37,6 @@ UNIFIED_SOURCES += [
"BTSerializationTest.cpp",
"BTSignedTreeHeadTest.cpp",
"BTVerificationTest.cpp",
"CTDiversityPolicyTest.cpp",
"CTLogVerifierTest.cpp",
"CTObjectsExtractorTest.cpp",
"CTPolicyEnforcerTest.cpp",

View File

@ -497,17 +497,10 @@ void GatherCertificateTransparencyTelemetry(
Telemetry::Accumulate(Telemetry::SSL_SCTS_PER_CONNECTION, sctsCount);
// Report CT Policy compliance by CA.
switch (info.policyCompliance) {
case ct::CTPolicyCompliance::Compliant:
break;
case ct::CTPolicyCompliance::NotEnoughScts:
case ct::CTPolicyCompliance::NotDiverseScts:
AccumulateTelemetryForRootCA(
Telemetry::SSL_CT_POLICY_NON_COMPLIANT_CONNECTIONS_BY_CA_2, rootCert);
break;
case ct::CTPolicyCompliance::Unknown:
default:
MOZ_ASSERT_UNREACHABLE("Unexpected CTPolicyCompliance type");
if (info.policyCompliance.isSome() &&
*info.policyCompliance != ct::CTPolicyCompliance::Compliant) {
AccumulateTelemetryForRootCA(
Telemetry::SSL_CT_POLICY_NON_COMPLIANT_CONNECTIONS_BY_CA_2, rootCert);
}
}

View File

@ -1028,12 +1028,12 @@ uint16_t TransportSecurityInfo::ConvertCertificateTransparencyInfoToStatus(
const mozilla::psm::CertificateTransparencyInfo& info) {
using mozilla::ct::CTPolicyCompliance;
if (!info.enabled) {
if (!info.enabled || info.policyCompliance.isNothing()) {
// CT disabled.
return nsITransportSecurityInfo::CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE;
}
switch (info.policyCompliance) {
switch (*info.policyCompliance) {
case CTPolicyCompliance::Compliant:
return nsITransportSecurityInfo::
CERTIFICATE_TRANSPARENCY_POLICY_COMPLIANT;
@ -1043,7 +1043,6 @@ uint16_t TransportSecurityInfo::ConvertCertificateTransparencyInfoToStatus(
case CTPolicyCompliance::NotDiverseScts:
return nsITransportSecurityInfo ::
CERTIFICATE_TRANSPARENCY_POLICY_NOT_DIVERSE_SCTS;
case CTPolicyCompliance::Unknown:
default:
MOZ_ASSERT_UNREACHABLE("Unexpected CTPolicyCompliance type");
}

View File

@ -26,11 +26,9 @@ registerCleanupFunction(() => {
function run_test() {
Services.prefs.setIntPref("security.pki.certificate_transparency.mode", 1);
add_tls_server_setup("BadCertAndPinningServer", "test_ct");
// These certificates have a validity period of 800 days, which is a little
// over 2 years and 2 months. This gets rounded down to 2 years (since it's
// less than 2 years and 3 months). Our policy requires N + 1 embedded SCTs,
// where N is 2 in this case. So, a policy-compliant certificate would have at
// least 3 SCTs.
// These certificates have a validity period of 800 days, which is greater
// than 180 days. Our policy requires 3 embedded SCTs for certificates with a
// validity period greater than 180 days.
add_connection_test(
"ct-valid.example.com",
PRErrorCodeSuccess,