// 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 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 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 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> 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() { static 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