blob: 946be9bf57478391d39feb706c4f4b6ac920752e [file] [log] [blame]
// 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() {
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