Commit 3d509763 authored by eranm's avatar eranm Committed by Commit bot

Certificate Transparency: Correct month calculation.

The number of SCTs required for certificates with a validity period of over 14 months (but under 15) was incorrect. Fixed by not counting partial months.

BUG=470169

Review URL: https://codereview.chromium.org/1032093002

Cr-Commit-Position: refs/heads/master@{#322501}
parent 19519140
...@@ -43,20 +43,34 @@ bool IsBuildTimely() { ...@@ -43,20 +43,34 @@ bool IsBuildTimely() {
#endif #endif
} }
uint32_t ApproximateMonthDifference(const base::Time& start, // Returns a rounded-down months difference of |start| and |end|,
const base::Time& end) { // together with an indication of whether the last month was
// a full month, because the range starts specified in the policy
// are not consistent in terms of including the range start value.
void RoundedDownMonthDifference(const base::Time& start,
const base::Time& end,
size_t* rounded_months_difference,
bool* has_partial_month) {
DCHECK(rounded_months_difference);
DCHECK(has_partial_month);
base::Time::Exploded exploded_start; base::Time::Exploded exploded_start;
base::Time::Exploded exploded_expiry; base::Time::Exploded exploded_expiry;
start.UTCExplode(&exploded_start); start.UTCExplode(&exploded_start);
end.UTCExplode(&exploded_expiry); end.UTCExplode(&exploded_expiry);
if (end < start) {
*rounded_months_difference = 0;
*has_partial_month = false;
}
*has_partial_month = true;
uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 + uint32_t month_diff = (exploded_expiry.year - exploded_start.year) * 12 +
(exploded_expiry.month - exploded_start.month); (exploded_expiry.month - exploded_start.month);
if (exploded_expiry.day_of_month < exploded_start.day_of_month)
--month_diff;
else if (exploded_expiry.day_of_month == exploded_start.day_of_month)
*has_partial_month = false;
// Add any remainder as a full month. *rounded_months_difference = month_diff;
if (exploded_expiry.day_of_month > exploded_start.day_of_month)
++month_diff;
return month_diff;
} }
bool HasRequiredNumberOfSCTs(const X509Certificate& cert, bool HasRequiredNumberOfSCTs(const X509Certificate& cert,
...@@ -81,18 +95,20 @@ bool HasRequiredNumberOfSCTs(const X509Certificate& cert, ...@@ -81,18 +95,20 @@ bool HasRequiredNumberOfSCTs(const X509Certificate& cert,
return false; return false;
} }
uint32_t expiry_in_months_approx = size_t lifetime;
ApproximateMonthDifference(cert.valid_start(), cert.valid_expiry()); bool has_partial_month;
RoundedDownMonthDifference(cert.valid_start(), cert.valid_expiry(), &lifetime,
&has_partial_month);
// For embedded SCTs, if the certificate has the number of SCTs specified in // For embedded SCTs, if the certificate has the number of SCTs specified in
// table 1 of the "Qualifying Certificate" section of the CT/EV policy, then // table 1 of the "Qualifying Certificate" section of the CT/EV policy, then
// it qualifies. // it qualifies.
size_t num_required_embedded_scts; size_t num_required_embedded_scts;
if (expiry_in_months_approx > 39) { if (lifetime > 39 || (lifetime == 39 && has_partial_month)) {
num_required_embedded_scts = 5; num_required_embedded_scts = 5;
} else if (expiry_in_months_approx > 27) { } else if (lifetime > 27 || (lifetime == 27 && has_partial_month)) {
num_required_embedded_scts = 4; num_required_embedded_scts = 4;
} else if (expiry_in_months_approx >= 15) { } else if (lifetime >= 15) {
num_required_embedded_scts = 3; num_required_embedded_scts = 3;
} else { } else {
num_required_embedded_scts = 2; num_required_embedded_scts = 2;
......
...@@ -67,6 +67,29 @@ class CertPolicyEnforcerTest : public ::testing::Test { ...@@ -67,6 +67,29 @@ class CertPolicyEnforcerTest : public ::testing::Test {
} }
} }
void CheckCertificateCompliesWithExactNumberOfEmbeddedSCTs(
const base::Time& start,
const base::Time& end,
size_t required_scts) {
scoped_refptr<X509Certificate> cert(
new X509Certificate("subject", "issuer", start, end));
ct::CTVerifyResult result;
for (size_t i = 0; i < required_scts - 1; ++i) {
FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED,
1, &result);
EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy(
cert.get(), nullptr, result, BoundNetLog()))
<< " for: " << (end - start).InDays() << " and " << required_scts
<< " scts=" << result.verified_scts.size() << " i=" << i;
}
FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1,
&result);
EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy(
cert.get(), nullptr, result, BoundNetLog()))
<< " for: " << (end - start).InDays() << " and " << required_scts
<< " scts=" << result.verified_scts.size();
}
protected: protected:
scoped_ptr<CertPolicyEnforcer> policy_enforcer_; scoped_ptr<CertPolicyEnforcer> policy_enforcer_;
scoped_refptr<X509Certificate> chain_; scoped_refptr<X509Certificate> chain_;
...@@ -140,31 +163,45 @@ TEST_F(CertPolicyEnforcerTest, DoesNotConformToPolicyInvalidDates) { ...@@ -140,31 +163,45 @@ TEST_F(CertPolicyEnforcerTest, DoesNotConformToPolicyInvalidDates) {
TEST_F(CertPolicyEnforcerTest, TEST_F(CertPolicyEnforcerTest,
ConformsToPolicyExactNumberOfSCTsForValidityPeriod) { ConformsToPolicyExactNumberOfSCTsForValidityPeriod) {
// Test multiple validity periods: Over 27 months, Over 15 months (but less // Test multiple validity periods
// than 27 months), const struct TestData {
// Less than 15 months. base::Time validity_start;
const size_t validity_period[] = {12, 19, 30, 50}; base::Time validity_end;
const size_t needed_scts[] = {2, 3, 4, 5}; size_t scts_required;
} kTestData[] = {{// Cert valid for 14 months, needs 2 SCTs.
for (int i = 0; i < 3; ++i) { base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
size_t curr_validity = validity_period[i]; base::Time::FromUTCExploded({2016, 6, 0, 6, 11, 25, 0, 0}),
scoped_refptr<X509Certificate> cert(new X509Certificate( 2},
"subject", "issuer", base::Time::Now(), {// Cert valid for exactly 15 months, needs 3 SCTs.
base::Time::Now() + base::TimeDelta::FromDays(31 * curr_validity))); base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
size_t curr_required_scts = needed_scts[i]; base::Time::FromUTCExploded({2016, 6, 0, 25, 11, 25, 0, 0}),
ct::CTVerifyResult result; 3},
for (size_t j = 0; j < curr_required_scts - 1; ++j) { {// Cert valid for over 15 months, needs 3 SCTs.
FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
1, &result); base::Time::FromUTCExploded({2016, 6, 0, 27, 11, 25, 0, 0}),
EXPECT_FALSE(policy_enforcer_->DoesConformToCTEVPolicy( 3},
cert.get(), nullptr, result, BoundNetLog())) {// Cert valid for exactly 27 months, needs 3 SCTs.
<< " for: " << curr_validity << " and " << curr_required_scts base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
<< " scts=" << result.verified_scts.size() << " j=" << j; base::Time::FromUTCExploded({2017, 6, 0, 25, 11, 25, 0, 0}),
} 3},
FillResultWithSCTsOfOrigin(ct::SignedCertificateTimestamp::SCT_EMBEDDED, 1, {// Cert valid for over 27 months, needs 4 SCTs.
&result); base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
EXPECT_TRUE(policy_enforcer_->DoesConformToCTEVPolicy( base::Time::FromUTCExploded({2017, 6, 0, 28, 11, 25, 0, 0}),
cert.get(), nullptr, result, BoundNetLog())); 4},
{// Cert valid for exactly 39 months, needs 4 SCTs.
base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
base::Time::FromUTCExploded({2018, 6, 0, 25, 11, 25, 0, 0}),
4},
{// Cert valid for over 39 months, needs 5 SCTs.
base::Time::FromUTCExploded({2015, 3, 0, 25, 11, 25, 0, 0}),
base::Time::FromUTCExploded({2018, 6, 0, 27, 11, 25, 0, 0}),
5}};
for (size_t i = 0; i < arraysize(kTestData); ++i) {
SCOPED_TRACE(i);
CheckCertificateCompliesWithExactNumberOfEmbeddedSCTs(
kTestData[i].validity_start, kTestData[i].validity_end,
kTestData[i].scts_required);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment