Bug 1794479 - Gather telemetry on the age of OCSP responses used to override CRLite. r=keeler

Defines the OCSP_AGE_AT_CRLITE_OVERRIDE histogram which records the age of an
OCSP response, in hours, when CRLite says a certificate is revoked and OCSP
says it's OK.

Differential Revision: https://phabricator.services.mozilla.com/D158991
This commit is contained in:
John Schanck 2022-10-13 14:08:23 +00:00
Родитель eb4bb28f8f
Коммит 59119c81d9
3 изменённых файлов: 52 добавлений и 6 удалений

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

@ -845,9 +845,10 @@ Result NSSCertDBTrustDomain::CheckRevocationByOCSP(
Result stapledOCSPResponseResult = Success;
if (stapledOCSPResponse) {
bool expired;
uint32_t ageInHours;
stapledOCSPResponseResult = VerifyAndMaybeCacheEncodedOCSPResponse(
certID, time, maxOCSPLifetimeInDays, *stapledOCSPResponse,
ResponseWasStapled, expired);
ResponseWasStapled, expired, ageInHours);
Telemetry::AccumulateCategorical(
Telemetry::LABELS_CERT_REVOCATION_MECHANISMS::StapledOCSP);
if (stapledOCSPResponseResult == Success) {
@ -1067,9 +1068,10 @@ Result NSSCertDBTrustDomain::SynchronousCheckRevocationWithServer(
// or unknown certificate, PR_GetError() will return the appropriate error.
// We actually ignore expired here.
bool expired;
rv = VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
maxOCSPLifetimeInDays, response,
ResponseIsFromNetwork, expired);
uint32_t ageInHours;
rv = VerifyAndMaybeCacheEncodedOCSPResponse(
certID, time, maxOCSPLifetimeInDays, response, ResponseIsFromNetwork,
expired, ageInHours);
// If the CRLite filter covers the certificate, compare the CRLite result
// with the OCSP fetching result. OCSP may have succeeded, said the
@ -1088,6 +1090,11 @@ Result NSSCertDBTrustDomain::SynchronousCheckRevocationWithServer(
// CRLite says the certificate is revoked, but OCSP says it is OK.
Telemetry::AccumulateCategorical(
Telemetry::LABELS_CRLITE_VS_OCSP_RESULT::CRLiteRevOCSPOk);
if (mCRLiteMode == CRLiteMode::ConfirmRevocations) {
Telemetry::Accumulate(Telemetry::OCSP_AGE_AT_CRLITE_OVERRIDE,
ageInHours);
}
}
} else if (rv == Result::ERROR_REVOKED_CERTIFICATE) {
if (crliteResult == Success) {
@ -1183,7 +1190,8 @@ Result NSSCertDBTrustDomain::HandleOCSPFailure(
Result NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
const CertID& certID, Time time, uint16_t maxLifetimeInDays,
Input encodedResponse, EncodedResponseSource responseSource,
/*out*/ bool& expired) {
/*out*/ bool& expired,
/*out*/ uint32_t& ageInHours) {
Time thisUpdate(Time::uninitialized);
Time validThrough(Time::uninitialized);
@ -1207,6 +1215,30 @@ Result NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
return Result::FATAL_ERROR_LIBRARY_FAILURE; // integer overflow
}
}
// The `thisUpdate` field holds the latest time at which the server knew the
// response was correct. The age of the response is the time that has elapsed
// since. We only use this for the telemetry defined in Bug 1794479.
uint64_t timeInSeconds;
uint64_t thisUpdateInSeconds;
uint64_t ageInSeconds;
SecondsSinceEpochFromTime(time, &timeInSeconds);
SecondsSinceEpochFromTime(thisUpdate, &thisUpdateInSeconds);
if (timeInSeconds >= thisUpdateInSeconds) {
ageInSeconds = timeInSeconds - thisUpdateInSeconds;
// ageInHours is 32 bits because of the telemetry api.
if (ageInSeconds > UINT32_MAX) {
// We could divide by 3600 before checking the UINT32_MAX bound, but if
// ageInSeconds is more than UINT32_MAX then there's been some sort of
// error.
ageInHours = UINT32_MAX;
} else {
// We start at 1 and divide with truncation to reserve ageInHours=0 for
// the case where `thisUpdate` is in the future.
ageInHours = 1 + ageInSeconds / (60 * 60);
}
} else {
ageInHours = 0;
}
if (responseSource == ResponseIsFromNetwork || rv == Success ||
rv == Result::ERROR_REVOKED_CERTIFICATE ||
rv == Result::ERROR_OCSP_UNKNOWN_CERT) {

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

@ -264,7 +264,8 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain {
Result VerifyAndMaybeCacheEncodedOCSPResponse(
const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
uint16_t maxLifetimeInDays, mozilla::pkix::Input encodedResponse,
EncodedResponseSource responseSource, /*out*/ bool& expired);
EncodedResponseSource responseSource, /*out*/ bool& expired,
/*out*/ uint32_t& ageInHours);
TimeDuration GetOCSPTimeout() const;
Result CheckRevocationByCRLite(const mozilla::pkix::CertID& certID,

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

@ -3415,6 +3415,19 @@
"n_values": 16,
"description": "SSL Handshake Key Exchange Algorithm for resumed handshake (null=0, rsa=1, dh=2, fortezza=3, ecdh=4)"
},
"OCSP_AGE_AT_CRLITE_OVERRIDE": {
"record_in_processes": ["main", "socket"],
"products": ["firefox", "fennec"],
"alert_emails": ["seceng-telemetry@mozilla.com", "jschanck@mozilla.com"],
"bug_numbers": [1794479],
"expires_in_version": "113",
"kind": "linear",
"releaseChannelCollection": "opt-out",
"low": 1,
"high": 240,
"n_buckets": 20,
"description": "When OCSP and CRLite differ, how old is the OCSP response (in hours)?"
},
"CRLITE_VS_OCSP_RESULT": {
"record_in_processes": ["main", "socket"],
"products": ["firefox", "fennec"],