Skip to content

Commit

Permalink
Record more TPM metrics
Browse files Browse the repository at this point in the history
This CL adds the following events
- Latency for create, restore from wrapped and sign message
- Success/failure and algo for each action

See:
Latency: https://screenshot.googleplex.com/9zbPzLDsuPX7CEh
Success: https://screenshot.googleplex.com/4WhUsF7chKeo9Q7

Bug: 1341286
Change-Id: I274480986332e97a3bfde22739e001df9d7842be
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3884723
Reviewed-by: Alex Ilin <alexilin@chromium.org>
Reviewed-by: Adam Langley <agl@chromium.org>
Commit-Queue: Kristian Monsen <kristianm@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1047569}
  • Loading branch information
Kristian Monsen authored and Chromium LUCI CQ committed Sep 15, 2022
1 parent 5e150ee commit d39acd1
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_switches.h"
#include "crypto/unexportable_key.h"
#include "crypto/unexportable_key_metrics.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/browser_metrics.h"
#include "ui/base/pointer/pointer_device.h"
#include "ui/base/ui_base_switches.h"
Expand Down Expand Up @@ -486,7 +486,7 @@ void RecordStartupMetrics() {
// behavior.
base::UmaHistogramBoolean("Windows.ParallelDllLoadingEnabled",
IsParallelDllLoadingEnabled());
crypto::MeasureTPMAvailabilityWin();
crypto::MaybeMeasureTpmOperations();
#endif // BUILDFLAG(IS_WIN)

// Record whether Chrome is the default browser or not.
Expand Down
6 changes: 6 additions & 0 deletions crypto/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ component("crypto") {
"symmetric_key.h",
"unexportable_key.cc",
"unexportable_key.h",
"unexportable_key_metrics.cc",
"unexportable_key_metrics.h",
]

deps = [
Expand Down Expand Up @@ -166,6 +168,10 @@ test("crypto_unittests") {
"unexportable_key_unittest.cc",
]

if (is_win) {
sources += [ "unexportable_key_metrics_unittest.cc" ]
}

# Some files are built when NSS is used for the platform certificate library.
if (use_nss_certs) {
sources += [
Expand Down
61 changes: 0 additions & 61 deletions crypto/unexportable_key.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@
#include "base/check.h"
#include "build/build_config.h"

#if BUILDFLAG(IS_WIN)
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/timer/elapsed_timer.h"
#endif // BUILDFLAG(IS_WIN)

namespace crypto {

namespace {
Expand All @@ -41,59 +33,6 @@ std::unique_ptr<UnexportableKeyProvider> GetUnexportableKeyProvider() {
#endif
}

#if BUILDFLAG(IS_WIN)
void MeasureTPMAvailabilityWin() {
// Measure the fraction of Windows machines that have TPMs, and what the best
// supported algorithm is.
base::ThreadPool::PostTask(
// GetUnexportableKeyProvider can call functions that take the global
// loader lock, so although BEST_EFFORT makes it low priority to start,
// once it starts it must run in a foreground thread to avoid priority
// inversions.
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::ThreadPolicy::MUST_USE_FOREGROUND},
base::BindOnce([]() {
// Note that values here are used in a recorded histogram. Don't change
// the values of existing members.
enum TPMSupport {
kNone = 0,
kRSA = 1,
kECDSA = 2,
kMaxValue = 2,
};

TPMSupport result = TPMSupport::kNone;
std::unique_ptr<UnexportableKeyProvider> provider =
GetUnexportableKeyProvider();
if (provider) {
const SignatureVerifier::SignatureAlgorithm kAllAlgorithms[] = {
SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256,
SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256,
};
auto algo = provider->SelectAlgorithm(kAllAlgorithms);
if (algo) {
switch (*algo) {
case SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256:
result = TPMSupport::kECDSA;
break;
case SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256:
result = TPMSupport::kRSA;
break;
case SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA1:
case SignatureVerifier::SignatureAlgorithm::RSA_PSS_SHA256:
// Not supported for this metric.
break;
}
}
}
// This metric was previously named Crypto.TPMSupport but that expired,
// so using a new name to avoid mixing up with old data.
base::UmaHistogramEnumeration("Crypto.TPMSupport2", result);
}));
}
#endif // BUILDFLAG(IS_WIN)

namespace internal {

void SetUnexportableKeyProviderForTesting(
Expand Down
5 changes: 0 additions & 5 deletions crypto/unexportable_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,6 @@ class CRYPTO_EXPORT UnexportableKeyProvider {
CRYPTO_EXPORT std::unique_ptr<UnexportableKeyProvider>
GetUnexportableKeyProvider();

#if BUILDFLAG(IS_WIN)
// MeasureTPMAvailability records UMA metrics of TPM availability.
CRYPTO_EXPORT void MeasureTPMAvailabilityWin();
#endif // BUILDFLAG(IS_WIN)

namespace internal {

CRYPTO_EXPORT void SetUnexportableKeyProviderForTesting(
Expand Down
178 changes: 178 additions & 0 deletions crypto/unexportable_key_metrics.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "crypto/unexportable_key_metrics.h"

#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/timer/elapsed_timer.h"
#include "crypto/unexportable_key.h"

namespace crypto {

namespace {

enum class TPMOperation {
kMessageSigning,
kMessageVerify,
kWrappedKeyCreation,
kNewKeyCreation,
};

std::string GetHistogramSuffixForOperation(TPMOperation operation) {
switch (operation) {
case TPMOperation::kMessageSigning:
return "MessageSigning";
case TPMOperation::kMessageVerify:
return "MessageVerify";
case TPMOperation::kNewKeyCreation:
return "NewKeyCreation";
case TPMOperation::kWrappedKeyCreation:
return "WrappedKeyCreation";
}
return "";
}

std::string GetHistogramSuffixForAlgo(internal::TPMSupport algo) {
switch (algo) {
case internal::TPMSupport::kECDSA:
return "ECDSA";
case internal::TPMSupport::kRSA:
return "RSA";
case internal::TPMSupport::kNone:
return "";
}
return "";
}

void ReportUmaLatency(TPMOperation operation,
internal::TPMSupport algo,
base::TimeDelta latency) {
std::string histogram_name = "Crypto.TPMDuration." +
GetHistogramSuffixForOperation(operation) +
GetHistogramSuffixForAlgo(algo);
base::UmaHistogramMediumTimes(histogram_name, latency);
}

void ReportUmaOperationSuccess(TPMOperation operation,
internal::TPMSupport algo,
bool status) {
std::string histogram_name = "Crypto.TPMOperation." +
GetHistogramSuffixForOperation(operation) +
GetHistogramSuffixForAlgo(algo);
base::UmaHistogramBoolean(histogram_name, status);
}

void ReportUmaTpmOperation(TPMOperation operation,
internal::TPMSupport algo,
base::TimeDelta latency,
bool status) {
ReportUmaOperationSuccess(operation, algo, status);
if (status && operation != TPMOperation::kMessageVerify) {
// Only report latency for successful operations
// No latency reported for verification that is done outside of TPM
ReportUmaLatency(operation, algo, latency);
}
}

void MeasureTpmOperationsInternal() {
internal::TPMSupport supported_algo = internal::TPMSupport::kNone;
std::unique_ptr<UnexportableKeyProvider> provider =
GetUnexportableKeyProvider();
if (!provider) {
return;
}

const SignatureVerifier::SignatureAlgorithm kAllAlgorithms[] = {
SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256,
SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256,
};

auto algo = provider->SelectAlgorithm(kAllAlgorithms);
if (algo) {
switch (*algo) {
case SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256:
supported_algo = internal::TPMSupport::kECDSA;
break;
case SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256:
supported_algo = internal::TPMSupport::kRSA;
break;
case SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA1:
case SignatureVerifier::SignatureAlgorithm::RSA_PSS_SHA256:
// Not supported for this metric.
break;
}
}

// Report if TPM is supported and best algo
base::UmaHistogramEnumeration("Crypto.TPMSupport2", supported_algo);
if (supported_algo == internal::TPMSupport::kNone) {
return;
}

base::ElapsedTimer key_creation_timer;
std::unique_ptr<UnexportableSigningKey> current_key =
provider->GenerateSigningKeySlowly(kAllAlgorithms);
ReportUmaTpmOperation(TPMOperation::kNewKeyCreation, supported_algo,
key_creation_timer.Elapsed(), current_key != nullptr);
if (!current_key) {
return;
}

base::ElapsedTimer wrapped_key_creation_timer;
std::unique_ptr<UnexportableSigningKey> wrapped_key =
provider->FromWrappedSigningKeySlowly(current_key->GetWrappedKey());
ReportUmaTpmOperation(TPMOperation::kWrappedKeyCreation, supported_algo,
wrapped_key_creation_timer.Elapsed(),
wrapped_key != nullptr);

const uint8_t msg[] = {1, 2, 3, 4};
base::ElapsedTimer message_signing_timer;
absl::optional<std::vector<uint8_t>> signed_bytes =
current_key->SignSlowly(msg);
ReportUmaTpmOperation(TPMOperation::kMessageSigning, supported_algo,
message_signing_timer.Elapsed(),
signed_bytes.has_value());
if (!signed_bytes.has_value()) {
return;
}

crypto::SignatureVerifier verifier;
bool verify_init =
verifier.VerifyInit(current_key->Algorithm(), signed_bytes.value(),
current_key->GetSubjectPublicKeyInfo());
if (verify_init) {
verifier.VerifyUpdate(msg);
bool verify_final = verifier.VerifyFinal();
ReportUmaOperationSuccess(TPMOperation::kMessageVerify, supported_algo,
verify_final);
} else {
ReportUmaOperationSuccess(TPMOperation::kMessageVerify, supported_algo,
verify_init);
}
}

} // namespace

namespace internal {

void MeasureTpmOperationsInternalForTesting() {
MeasureTpmOperationsInternal();
}

} // namespace internal

void MaybeMeasureTpmOperations() {
const base::Feature kTpmLatencyMetrics{"TpmLatencyMetrics",
base::FEATURE_ENABLED_BY_DEFAULT};
if (base::FeatureList::IsEnabled(kTpmLatencyMetrics)) {
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&MeasureTpmOperationsInternal));
}
}

} // namespace crypto
34 changes: 34 additions & 0 deletions crypto/unexportable_key_metrics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CRYPTO_UNEXPORTABLE_KEY_METRICS_H_
#define CRYPTO_UNEXPORTABLE_KEY_METRICS_H_

#include "crypto/crypto_export.h"

namespace crypto {

// Records UMA metrics of TPM availability, latency and successful usage.
// Does the work on a new background task.
CRYPTO_EXPORT void MaybeMeasureTpmOperations();

// internal namespace to be used by tests only
namespace internal {

// Note that values here are used in a recorded histogram. Don't change
// the values of existing members.
enum class TPMSupport {
kNone = 0,
kRSA = 1,
kECDSA = 2,
kMaxValue = 2,
};

// Exported for testing
CRYPTO_EXPORT void MeasureTpmOperationsInternalForTesting();
} // namespace internal

} // namespace crypto

#endif // CRYPTO_UNEXPORTABLE_KEY_METRICS_H_
57 changes: 57 additions & 0 deletions crypto/unexportable_key_metrics_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "crypto/unexportable_key_metrics.h"

#include "base/test/metrics/histogram_tester.h"
#include "crypto/scoped_mock_unexportable_key_provider.h"
#include "crypto/signature_verifier.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

// Note mock provider only supports ECDSA
TEST(UnexportableKeyMetricTest, GatherAllMetrics) {
crypto::ScopedMockUnexportableKeyProvider scoped_mock_key_provider;

base::HistogramTester histogram_tester;
histogram_tester.ExpectTotalCount("Crypto.TPMSupport2", 0);
histogram_tester.ExpectTotalCount("Crypto.TPMDuration.NewKeyCreationECDSA",
0);
histogram_tester.ExpectTotalCount(
"Crypto.TPMDuration.WrappedKeyCreationECDSA", 0);
histogram_tester.ExpectTotalCount("Crypto.TPMDuration.MessageSigningECDSA",
0);
histogram_tester.ExpectTotalCount("Crypto.TPMOperation.NewKeyCreation", 0);
histogram_tester.ExpectTotalCount("Crypto.TPMOperation.WrappedKeyCreation",
0);
histogram_tester.ExpectTotalCount("Crypto.TPMOperation.MessageSigning", 0);
histogram_tester.ExpectTotalCount("Crypto.TPMOperation.MessageVerify", 0);

crypto::internal::MeasureTpmOperationsInternalForTesting();

EXPECT_THAT(
histogram_tester.GetAllSamples("Crypto.TPMSupport2"),
BucketsAre(base::Bucket(crypto::internal::TPMSupport::kECDSA, 1)));
histogram_tester.ExpectTotalCount("Crypto.TPMDuration.NewKeyCreationECDSA",
1);
histogram_tester.ExpectTotalCount(
"Crypto.TPMDuration.WrappedKeyCreationECDSA", 1);
histogram_tester.ExpectTotalCount("Crypto.TPMDuration.MessageSigningECDSA",
1);
EXPECT_THAT(
histogram_tester.GetAllSamples("Crypto.TPMOperation.NewKeyCreationECDSA"),
BucketsAre(base::Bucket(true, 1)));
EXPECT_THAT(histogram_tester.GetAllSamples(
"Crypto.TPMOperation.WrappedKeyCreationECDSA"),
BucketsAre(base::Bucket(true, 1)));
EXPECT_THAT(
histogram_tester.GetAllSamples("Crypto.TPMOperation.MessageSigningECDSA"),
BucketsAre(base::Bucket(true, 1)));
EXPECT_THAT(
histogram_tester.GetAllSamples("Crypto.TPMOperation.MessageVerifyECDSA"),
BucketsAre(base::Bucket(true, 1)));
}

} // namespace
Loading

0 comments on commit d39acd1

Please sign in to comment.