| // 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<memory> |
| |
| #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{ |
| |
| enumclassKeyType{ |
| kHardwareKey, |
| kVirtualizedKey, |
| }; |
| |
| constSignatureVerifier::SignatureAlgorithm kAllAlgorithms[]={ |
| SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256, |
| SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256, |
| }; |
| |
| constexprchar kTestKeyName[]="ChromeMetricsTestKey"; |
| |
| // Leaving HW empty will keep the existing metric as is today. |
| std::stringGetHistogramPrefixForKeyType(KeyType type){ |
| switch(type){ |
| caseKeyType::kHardwareKey: |
| return""; |
| caseKeyType::kVirtualizedKey: |
| return"Virtual."; |
| } |
| } |
| |
| std::stringGetHistogramSuffixForAlgo(internal::TPMSupport algo){ |
| switch(algo){ |
| case internal::TPMSupport::kECDSA: |
| return"ECDSA"; |
| case internal::TPMSupport::kRSA: |
| return"RSA"; |
| case internal::TPMSupport::kNone: |
| return""; |
| } |
| return""; |
| } |
| |
| internal::TPMTypeGetSupportedTpm(internal::TPMSupport hw, |
| internal::TPMSupport virt){ |
| if(hw!= internal::TPMSupport::kNone&& |
| virt!= internal::TPMSupport::kNone){ |
| return internal::TPMType::kBoth; |
| } |
| |
| if(hw!= internal::TPMSupport::kNone){ |
| return internal::TPMType::kHW; |
| } |
| |
| // This is not expected |
| if(virt!= internal::TPMSupport::kNone){ |
| return internal::TPMType::kVirtual; |
| } |
| |
| return internal::TPMType::kNone; |
| } |
| |
| voidReportUmaLatency(TPMOperation operation, |
| internal::TPMSupport algo, |
| base::TimeDelta latency, |
| KeyType type=KeyType::kHardwareKey){ |
| std::string histogram_name= |
| "Crypto.TPMDuration."+GetHistogramPrefixForKeyType(type)+ |
| OperationToString(operation)+GetHistogramSuffixForAlgo(algo); |
| base::UmaHistogramMediumTimes(histogram_name, latency); |
| } |
| |
| voidReportUmaOperationSuccess(TPMOperation operation, |
| internal::TPMSupport algo, |
| bool status, |
| KeyType type=KeyType::kHardwareKey){ |
| std::string histogram_name= |
| "Crypto.TPMOperation."+GetHistogramPrefixForKeyType(type)+ |
| OperationToString(operation)+GetHistogramSuffixForAlgo(algo); |
| base::UmaHistogramBoolean(histogram_name, status); |
| } |
| |
| voidReportUmaTpmOperation(TPMOperation operation, |
| internal::TPMSupport algo, |
| base::TimeDelta latency, |
| bool status, |
| KeyType type=KeyType::kHardwareKey){ |
| ReportUmaOperationSuccess(operation, algo, status, type); |
| 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, type); |
| } |
| } |
| |
| internal::TPMSupportMeasureVirtualTpmOperations(){ |
| internal::TPMSupport supported_virtual_algo= internal::TPMSupport::kNone; |
| std::unique_ptr<VirtualUnexportableKeyProvider> virtual_provider= |
| GetVirtualUnexportableKeyProvider_DO_NOT_USE_METRICS_ONLY(); |
| |
| if(!virtual_provider){ |
| return supported_virtual_algo; |
| } |
| |
| auto algo= virtual_provider->SelectAlgorithm(kAllAlgorithms); |
| if(algo){ |
| switch(*algo){ |
| caseSignatureVerifier::SignatureAlgorithm::ECDSA_SHA256: |
| supported_virtual_algo= internal::TPMSupport::kECDSA; |
| break; |
| caseSignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256: |
| supported_virtual_algo= internal::TPMSupport::kRSA; |
| break; |
| caseSignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA1: |
| caseSignatureVerifier::SignatureAlgorithm::RSA_PSS_SHA256: |
| // Not supported for this metric. |
| break; |
| } |
| } |
| |
| // Report if virtual TPM is supported and best algo |
| base::UmaHistogramEnumeration("Crypto.VirtualKeySupport", |
| supported_virtual_algo); |
| |
| base::ElapsedTimer key_creation_timer; |
| std::unique_ptr<VirtualUnexportableSigningKey> current_key= |
| virtual_provider->GenerateSigningKey(kAllAlgorithms, kTestKeyName); |
| ReportUmaTpmOperation(TPMOperation::kNewKeyCreation, supported_virtual_algo, |
| key_creation_timer.Elapsed(), current_key!=nullptr, |
| KeyType::kVirtualizedKey); |
| if(!current_key){ |
| // Report no support if keys cannot be created, Windows appears to always |
| // mark the keys as available in SelectAlgorithm. |
| return internal::TPMSupport::kNone; |
| } |
| |
| base::ElapsedTimer open_key_timer; |
| std::string key_name= current_key->GetKeyName(); |
| std::unique_ptr<VirtualUnexportableSigningKey> opened_key= |
| virtual_provider->FromKeyName(key_name); |
| // Re-using TPMOperation::kWrappedKeyCreation for restoring keys even though |
| // there are no wrapped keys involved. |
| ReportUmaTpmOperation(TPMOperation::kWrappedKeyCreation, |
| supported_virtual_algo, open_key_timer.Elapsed(), |
| opened_key!=nullptr,KeyType::kVirtualizedKey); |
| |
| constuint8_t msg[]={1,2,3,4}; |
| base::ElapsedTimer message_signing_timer; |
| std::optional<std::vector<uint8_t>> signed_bytes= current_key->Sign(msg); |
| ReportUmaTpmOperation(TPMOperation::kMessageSigning, supported_virtual_algo, |
| message_signing_timer.Elapsed(), |
| signed_bytes.has_value(),KeyType::kVirtualizedKey); |
| |
| if(signed_bytes.has_value()){ |
| 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_virtual_algo, verify_final, |
| KeyType::kVirtualizedKey); |
| }else{ |
| ReportUmaOperationSuccess(TPMOperation::kMessageVerify, |
| supported_virtual_algo, verify_init, |
| KeyType::kVirtualizedKey); |
| } |
| } |
| |
| current_key.get()->DeleteKey(); |
| return supported_virtual_algo; |
| } |
| |
| voidMeasureTpmOperationsInternal(UnexportableKeyProvider::Config config){ |
| internal::TPMSupport supported_algo= internal::TPMSupport::kNone; |
| std::unique_ptr<UnexportableKeyProvider> provider= |
| GetUnexportableKeyProvider(std::move(config)); |
| if(!provider){ |
| base::UmaHistogramEnumeration("Crypto.TPMSupportType", supported_algo); |
| return; |
| } |
| |
| auto algo= provider->SelectAlgorithm(kAllAlgorithms); |
| if(algo){ |
| switch(*algo){ |
| caseSignatureVerifier::SignatureAlgorithm::ECDSA_SHA256: |
| supported_algo= internal::TPMSupport::kECDSA; |
| break; |
| caseSignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256: |
| supported_algo= internal::TPMSupport::kRSA; |
| break; |
| caseSignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA1: |
| caseSignatureVerifier::SignatureAlgorithm::RSA_PSS_SHA256: |
| // Not supported for this metric. |
| break; |
| } |
| } |
| |
| internal::TPMSupport supported_virtual_algo=MeasureVirtualTpmOperations(); |
| base::UmaHistogramEnumeration( |
| "Crypto.TPMSupportType", |
| GetSupportedTpm(supported_algo, supported_virtual_algo)); |
| |
| // Report if TPM is supported and best algo |
| base::UmaHistogramEnumeration("Crypto.TPMSupport2", supported_algo); |
| if(supported_algo== internal::TPMSupport::kNone){ |
| return; |
| } |
| |
| auto delete_key=[&provider](UnexportableSigningKey* key){ |
| provider->DeleteSigningKeySlowly(key->GetWrappedKey()); |
| delete key; |
| }; |
| base::ElapsedTimer key_creation_timer; |
| std::unique_ptr<UnexportableSigningKey,decltype(delete_key)> current_key( |
| provider->GenerateSigningKeySlowly(kAllAlgorithms).release(), delete_key); |
| 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,decltype(delete_key)> wrapped_key( |
| provider->FromWrappedSigningKeySlowly(current_key->GetWrappedKey()) |
| .release(), |
| delete_key); |
| ReportUmaTpmOperation(TPMOperation::kWrappedKeyCreation, supported_algo, |
| wrapped_key_creation_timer.Elapsed(), |
| wrapped_key!=nullptr); |
| |
| constuint8_t msg[]={1,2,3,4}; |
| base::ElapsedTimer message_signing_timer; |
| std::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{ |
| |
| voidMeasureTpmOperationsInternalForTesting(){ |
| MeasureTpmOperationsInternal(/*config=*/{}); |
| } |
| |
| }// namespace internal |
| |
| std::stringOperationToString(TPMOperation operation){ |
| switch(operation){ |
| caseTPMOperation::kMessageSigning: |
| return"MessageSigning"; |
| caseTPMOperation::kMessageVerify: |
| return"MessageVerify"; |
| caseTPMOperation::kNewKeyCreation: |
| return"NewKeyCreation"; |
| caseTPMOperation::kWrappedKeyCreation: |
| return"WrappedKeyCreation"; |
| caseTPMOperation::kWrappedKeyExport: |
| return"WrappedKeyExport"; |
| } |
| } |
| |
| std::stringAlgorithmToString(SignatureVerifier::SignatureAlgorithm algorithm){ |
| switch(algorithm){ |
| caseSignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA1: |
| caseSignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256: |
| caseSignatureVerifier::SignatureAlgorithm::RSA_PSS_SHA256: |
| return"RSA"; |
| caseSignatureVerifier::SignatureAlgorithm::ECDSA_SHA256: |
| return"ECDSA"; |
| } |
| } |
| |
| voidMaybeMeasureTpmOperations(UnexportableKeyProvider::Config config){ |
| base::ThreadPool::PostTask( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce(&MeasureTpmOperationsInternal, std::move(config))); |
| } |
| |
| }// namespace crypto |