Bug 1320567 - Certificate Transparency - telemetry reports of CT Policy compliance. r=keeler draft
authorSergei Chernov <sergei.cv@ndivi.com>
Mon, 30 Jan 2017 18:16:49 +0200
changeset 485373 47836d0f8e13147778cd2d792bdbc1d97d298610
parent 468005 f7e1982a2582b14c5885d787b530f879da3a040e
child 545996 9117639fe7f9ba0fc4627b533c9c3361c77f9652
push id45709
push usersergei.cv@ndivi.com
push dateThu, 16 Feb 2017 16:05:36 +0000
reviewerskeeler
bugs1320567
milestone54.0a1
Bug 1320567 - Certificate Transparency - telemetry reports of CT Policy compliance. r=keeler MozReview-Commit-ID: GtIz5O3FJ6H
security/manager/ssl/SSLServerCertVerification.cpp
toolkit/components/telemetry/Histograms.json
toolkit/components/telemetry/histogram-whitelists.json
--- a/security/manager/ssl/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/SSLServerCertVerification.cpp
@@ -1266,16 +1266,17 @@ GatherTelemetryForSingleSCT(const ct::Ve
       MOZ_ASSERT_UNREACHABLE("Unexpected VerifiedSCT::Status type");
   }
   Telemetry::Accumulate(Telemetry::SSL_SCTS_VERIFICATION_STATUS,
                         verificationStatus);
 }
 
 void
 GatherCertificateTransparencyTelemetry(const UniqueCERTCertList& certList,
+                                       bool isEV,
                                        const CertificateTransparencyInfo& info)
 {
   if (!info.enabled) {
     // No telemetry is gathered when CT is disabled.
     return;
   }
 
   for (const ct::VerifiedSCT& sct : info.verifyResult.verifiedScts) {
@@ -1289,16 +1290,69 @@ GatherCertificateTransparencyTelemetry(c
   }
 
   // Handle the histogram of SCTs counts.
   uint32_t sctsCount =
     static_cast<uint32_t>(info.verifyResult.verifiedScts.length());
   // Note that sctsCount can also be 0 in case we've received SCT binary data,
   // but it failed to parse (e.g. due to unsupported CT protocol version).
   Telemetry::Accumulate(Telemetry::SSL_SCTS_PER_CONNECTION, sctsCount);
+
+  // Report CT Policy compliance of EV certificates.
+  if (isEV) {
+    uint32_t evCompliance = 0;
+    switch (info.policyCompliance) {
+      case ct::CTPolicyCompliance::Compliant:
+        evCompliance = 1;
+        break;
+      case ct::CTPolicyCompliance::NotEnoughScts:
+        evCompliance = 2;
+        break;
+      case ct::CTPolicyCompliance::NotDiverseScts:
+        evCompliance = 3;
+        break;
+      case ct::CTPolicyCompliance::Unknown:
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unexpected CTPolicyCompliance type");
+    }
+    Telemetry::Accumulate(Telemetry::SSL_CT_POLICY_COMPLIANCE_OF_EV_CERTS,
+                          evCompliance);
+  }
+
+  // Get the root cert.
+  CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
+  MOZ_ASSERT(rootNode);
+  if (!rootNode) {
+    return;
+  }
+  MOZ_ASSERT(!CERT_LIST_END(rootNode, certList));
+  if (CERT_LIST_END(rootNode, certList)) {
+    return;
+  }
+  CERTCertificate* rootCert = rootNode->cert;
+  MOZ_ASSERT(rootCert);
+  if (!rootCert) {
+    return;
+  }
+
+  // Report CT Policy compliance by CA.
+  switch (info.policyCompliance) {
+    case ct::CTPolicyCompliance::Compliant:
+      AccumulateTelemetryForRootCA(
+        Telemetry::SSL_CT_POLICY_COMPLIANT_CONNECTIONS_BY_CA, rootCert);
+      break;
+    case ct::CTPolicyCompliance::NotEnoughScts:
+    case ct::CTPolicyCompliance::NotDiverseScts:
+      AccumulateTelemetryForRootCA(
+        Telemetry::SSL_CT_POLICY_NON_COMPLIANT_CONNECTIONS_BY_CA, rootCert);
+      break;
+    case ct::CTPolicyCompliance::Unknown:
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unexpected CTPolicyCompliance type");
+  }
 }
 
 // Note: Takes ownership of |peerCertChain| if SECSuccess is not returned.
 SECStatus
 AuthCertificate(CertVerifier& certVerifier,
                 nsNSSSocketInfo* infoObject,
                 const UniqueCERTCertificate& cert,
                 UniqueCERTCertList& peerCertChain,
@@ -1373,16 +1427,17 @@ AuthCertificate(CertVerifier& certVerifi
   if (rv == Success) {
     // Certificate verification succeeded. Delete any potential record of
     // certificate error bits.
     RememberCertErrorsTable::GetInstance().RememberCertHasError(infoObject,
                                                                 nullptr,
                                                                 SECSuccess);
     GatherSuccessfulValidationTelemetry(certList);
     GatherCertificateTransparencyTelemetry(certList,
+                                  /*isEV*/ evOidPolicy != SEC_OID_UNKNOWN,
                                            certificateTransparencyInfo);
 
     // The connection may get terminated, for example, if the server requires
     // a client cert. Let's provide a minimal SSLStatus
     // to the caller that contains at least the cert and its status.
     RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
     if (!status) {
       status = new nsSSLStatus();
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8498,16 +8498,43 @@
   },
   "SSL_CERT_VERIFICATION_ERRORS": {
     "alert_emails": ["seceng@mozilla.org"],
     "expires_in_version": "default",
     "kind": "enumerated",
     "n_values": 100,
     "description": "If certificate verification failed in a TLS handshake, what was the error? (see MapCertErrorToProbeValue in security/manager/ssl/SSLServerCertVerification.cpp and the values in security/pkix/include/pkix/Result.h)"
   },
+  "SSL_CT_POLICY_COMPLIANCE_OF_EV_CERTS": {
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
+    "expires_in_version": "62",
+    "kind": "enumerated",
+    "n_values": 10,
+    "bug_numbers": [1320567],
+    "releaseChannelCollection": "opt-out",
+    "description": "Certificate Transparency Policy compliance of successfully established SSL connections with EV certificate (1=Compliant, 2=Insufficient number of SCTs, 3=Insufficient diversity of CT Log operators)"
+  },
+  "SSL_CT_POLICY_COMPLIANT_CONNECTIONS_BY_CA": {
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 256,
+    "bug_numbers": [1320567],
+    "releaseChannelCollection": "opt-out",
+    "description": "Number of successfully established TLS connections compliant with the Certificate Transparency Policy, by CA. See https://dxr.mozilla.org/mozilla-central/source/security/manager/ssl/RootHashes.inc for names of CAs. Bucket zero holds CAs not present in the list."
+  },
+  "SSL_CT_POLICY_NON_COMPLIANT_CONNECTIONS_BY_CA": {
+    "alert_emails": ["seceng-telemetry@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 256,
+    "bug_numbers": [1320567],
+    "releaseChannelCollection": "opt-out",
+    "description": "Number of successfully established TLS connections NOT compliant with the Certificate Transparency Policy, by CA. See https://dxr.mozilla.org/mozilla-central/source/security/manager/ssl/RootHashes.inc for names of CAs. Bucket zero holds CAs not present in the list."
+  },
   "SSL_PERMANENT_CERT_ERROR_OVERRIDES": {
     "alert_emails": ["seceng@mozilla.org"],
     "expires_in_version": "default",
     "kind": "exponential",
     "high": 1024,
     "n_buckets": 10,
     "description": "How many permanent certificate overrides a user has stored."
   },
--- a/toolkit/components/telemetry/histogram-whitelists.json
+++ b/toolkit/components/telemetry/histogram-whitelists.json
@@ -1821,17 +1821,19 @@
     "SSL_CERT_VERIFICATION_ERRORS",
     "CERT_VALIDATION_SUCCESS_BY_CA",
     "CERT_PINNING_FAILURES_BY_CA",
     "CERT_PINNING_MOZ_RESULTS_BY_HOST",
     "CERT_PINNING_MOZ_TEST_RESULTS_BY_HOST",
     "GFX_CRASH",
     "GC_REASON_2",
     "GC_MINOR_REASON",
-    "GC_MINOR_REASON_LONG"
+    "GC_MINOR_REASON_LONG",
+    "SSL_CT_POLICY_COMPLIANT_CONNECTIONS_BY_CA",
+    "SSL_CT_POLICY_NON_COMPLIANT_CONNECTIONS_BY_CA"
   ],
   "expiry_default": [
     "A11Y_CONSUMERS",
     "IDLE_NOTIFY_IDLE_MS",
     "CACHE_MEMORY_SEARCH_2",
     "OSFILE_WORKER_LAUNCH_MS",
     "GEOLOCATION_OSX_SOURCE_IS_MLS",
     "TRANSLATION_OPPORTUNITIES_BY_LANGUAGE",