xref: /aosp_15_r20/external/cronet/crypto/unexportable_key_metrics.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "crypto/unexportable_key_metrics.h"
6 
7 #include <memory>
8 
9 #include "base/feature_list.h"
10 #include "base/metrics/histogram_functions.h"
11 #include "base/task/task_traits.h"
12 #include "base/task/thread_pool.h"
13 #include "base/timer/elapsed_timer.h"
14 #include "crypto/unexportable_key.h"
15 
16 namespace crypto {
17 
18 namespace {
19 
20 enum class TPMOperation {
21   kMessageSigning,
22   kMessageVerify,
23   kWrappedKeyCreation,
24   kNewKeyCreation,
25 };
26 
27 enum class KeyType {
28   kHardwareKey,
29   kVirtualizedKey,
30 };
31 
32 const SignatureVerifier::SignatureAlgorithm kAllAlgorithms[] = {
33     SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256,
34     SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256,
35 };
36 
37 constexpr char kTestKeyName[] = "ChromeMetricsTestKey";
38 
39 // Leaving HW empty will keep the existing metric as is today.
GetHistogramPrefixForKeyType(KeyType type)40 std::string GetHistogramPrefixForKeyType(KeyType type) {
41   switch (type) {
42     case KeyType::kHardwareKey:
43       return "";
44     case KeyType::kVirtualizedKey:
45       return "Virtual.";
46   }
47 }
48 
GetHistogramSuffixForOperation(TPMOperation operation)49 std::string GetHistogramSuffixForOperation(TPMOperation operation) {
50   switch (operation) {
51     case TPMOperation::kMessageSigning:
52       return "MessageSigning";
53     case TPMOperation::kMessageVerify:
54       return "MessageVerify";
55     case TPMOperation::kNewKeyCreation:
56       return "NewKeyCreation";
57     case TPMOperation::kWrappedKeyCreation:
58       return "WrappedKeyCreation";
59   }
60   return "";
61 }
62 
GetHistogramSuffixForAlgo(internal::TPMSupport algo)63 std::string GetHistogramSuffixForAlgo(internal::TPMSupport algo) {
64   switch (algo) {
65     case internal::TPMSupport::kECDSA:
66       return "ECDSA";
67     case internal::TPMSupport::kRSA:
68       return "RSA";
69     case internal::TPMSupport::kNone:
70       return "";
71   }
72   return "";
73 }
74 
GetSupportedTpm(internal::TPMSupport hw,internal::TPMSupport virt)75 internal::TPMType GetSupportedTpm(internal::TPMSupport hw,
76                                   internal::TPMSupport virt) {
77   if (hw != internal::TPMSupport::kNone &&
78       virt != internal::TPMSupport::kNone) {
79     return internal::TPMType::kBoth;
80   }
81 
82   if (hw != internal::TPMSupport::kNone) {
83     return internal::TPMType::kHW;
84   }
85 
86   // This is not expected
87   if (virt != internal::TPMSupport::kNone) {
88     return internal::TPMType::kVirtual;
89   }
90 
91   return internal::TPMType::kNone;
92 }
93 
ReportUmaLatency(TPMOperation operation,internal::TPMSupport algo,base::TimeDelta latency,KeyType type=KeyType::kHardwareKey)94 void ReportUmaLatency(TPMOperation operation,
95                       internal::TPMSupport algo,
96                       base::TimeDelta latency,
97                       KeyType type = KeyType::kHardwareKey) {
98   std::string histogram_name = "Crypto.TPMDuration." +
99                                GetHistogramPrefixForKeyType(type) +
100                                GetHistogramSuffixForOperation(operation) +
101                                GetHistogramSuffixForAlgo(algo);
102   base::UmaHistogramMediumTimes(histogram_name, latency);
103 }
104 
ReportUmaOperationSuccess(TPMOperation operation,internal::TPMSupport algo,bool status,KeyType type=KeyType::kHardwareKey)105 void ReportUmaOperationSuccess(TPMOperation operation,
106                                internal::TPMSupport algo,
107                                bool status,
108                                KeyType type = KeyType::kHardwareKey) {
109   std::string histogram_name = "Crypto.TPMOperation." +
110                                GetHistogramPrefixForKeyType(type) +
111                                GetHistogramSuffixForOperation(operation) +
112                                GetHistogramSuffixForAlgo(algo);
113   base::UmaHistogramBoolean(histogram_name, status);
114 }
115 
ReportUmaTpmOperation(TPMOperation operation,internal::TPMSupport algo,base::TimeDelta latency,bool status,KeyType type=KeyType::kHardwareKey)116 void ReportUmaTpmOperation(TPMOperation operation,
117                            internal::TPMSupport algo,
118                            base::TimeDelta latency,
119                            bool status,
120                            KeyType type = KeyType::kHardwareKey) {
121   ReportUmaOperationSuccess(operation, algo, status, type);
122   if (status && operation != TPMOperation::kMessageVerify) {
123     // Only report latency for successful operations
124     // No latency reported for verification that is done outside of TPM
125     ReportUmaLatency(operation, algo, latency, type);
126   }
127 }
128 
MeasureVirtualTpmOperations()129 internal::TPMSupport MeasureVirtualTpmOperations() {
130   internal::TPMSupport supported_virtual_algo = internal::TPMSupport::kNone;
131   std::unique_ptr<VirtualUnexportableKeyProvider> virtual_provider =
132       GetVirtualUnexportableKeyProvider_DO_NOT_USE_METRICS_ONLY();
133 
134   if (!virtual_provider) {
135     return supported_virtual_algo;
136   }
137 
138   auto algo = virtual_provider->SelectAlgorithm(kAllAlgorithms);
139   if (algo) {
140     switch (*algo) {
141       case SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256:
142         supported_virtual_algo = internal::TPMSupport::kECDSA;
143         break;
144       case SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256:
145         supported_virtual_algo = internal::TPMSupport::kRSA;
146         break;
147       case SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA1:
148       case SignatureVerifier::SignatureAlgorithm::RSA_PSS_SHA256:
149         // Not supported for this metric.
150         break;
151     }
152   }
153 
154   // Report if virtual TPM is supported and best algo
155   base::UmaHistogramEnumeration("Crypto.VirtualKeySupport",
156                                 supported_virtual_algo);
157 
158   base::ElapsedTimer key_creation_timer;
159   std::unique_ptr<VirtualUnexportableSigningKey> current_key =
160       virtual_provider->GenerateSigningKey(kAllAlgorithms, kTestKeyName);
161   ReportUmaTpmOperation(TPMOperation::kNewKeyCreation, supported_virtual_algo,
162                         key_creation_timer.Elapsed(), current_key != nullptr,
163                         KeyType::kVirtualizedKey);
164   if (!current_key) {
165     // Report no support if keys cannot be created, Windows appears to always
166     // mark the keys as available in SelectAlgorithm.
167     return internal::TPMSupport::kNone;
168   }
169 
170   base::ElapsedTimer open_key_timer;
171   std::string key_name = current_key->GetKeyName();
172   std::unique_ptr<VirtualUnexportableSigningKey> opened_key =
173       virtual_provider->FromKeyName(key_name);
174   // Re-using TPMOperation::kWrappedKeyCreation for restoring keys even though
175   // there are no wrapped keys involved.
176   ReportUmaTpmOperation(TPMOperation::kWrappedKeyCreation,
177                         supported_virtual_algo, open_key_timer.Elapsed(),
178                         opened_key != nullptr, KeyType::kVirtualizedKey);
179 
180   const uint8_t msg[] = {1, 2, 3, 4};
181   base::ElapsedTimer message_signing_timer;
182   std::optional<std::vector<uint8_t>> signed_bytes = current_key->Sign(msg);
183   ReportUmaTpmOperation(TPMOperation::kMessageSigning, supported_virtual_algo,
184                         message_signing_timer.Elapsed(),
185                         signed_bytes.has_value(), KeyType::kVirtualizedKey);
186 
187   if (signed_bytes.has_value()) {
188     crypto::SignatureVerifier verifier;
189     bool verify_init =
190         verifier.VerifyInit(current_key->Algorithm(), signed_bytes.value(),
191                             current_key->GetSubjectPublicKeyInfo());
192     if (verify_init) {
193       verifier.VerifyUpdate(msg);
194       bool verify_final = verifier.VerifyFinal();
195       ReportUmaOperationSuccess(TPMOperation::kMessageVerify,
196                                 supported_virtual_algo, verify_final,
197                                 KeyType::kVirtualizedKey);
198     } else {
199       ReportUmaOperationSuccess(TPMOperation::kMessageVerify,
200                                 supported_virtual_algo, verify_init,
201                                 KeyType::kVirtualizedKey);
202     }
203   }
204 
205   current_key.get()->DeleteKey();
206   return supported_virtual_algo;
207 }
208 
MeasureTpmOperationsInternal(UnexportableKeyProvider::Config config)209 void MeasureTpmOperationsInternal(UnexportableKeyProvider::Config config) {
210   internal::TPMSupport supported_algo = internal::TPMSupport::kNone;
211   std::unique_ptr<UnexportableKeyProvider> provider =
212       GetUnexportableKeyProvider(std::move(config));
213   if (!provider) {
214     return;
215   }
216 
217   auto algo = provider->SelectAlgorithm(kAllAlgorithms);
218   if (algo) {
219     switch (*algo) {
220       case SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256:
221         supported_algo = internal::TPMSupport::kECDSA;
222         break;
223       case SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256:
224         supported_algo = internal::TPMSupport::kRSA;
225         break;
226       case SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA1:
227       case SignatureVerifier::SignatureAlgorithm::RSA_PSS_SHA256:
228         // Not supported for this metric.
229         break;
230     }
231   }
232 
233   internal::TPMSupport supported_virtual_algo = MeasureVirtualTpmOperations();
234   base::UmaHistogramEnumeration(
235       "Crypto.TPMSupportType",
236       GetSupportedTpm(supported_algo, supported_virtual_algo));
237 
238   // Report if TPM is supported and best algo
239   base::UmaHistogramEnumeration("Crypto.TPMSupport2", supported_algo);
240   if (supported_algo == internal::TPMSupport::kNone) {
241     return;
242   }
243 
244   auto delete_key = [&provider](UnexportableSigningKey* key) {
245     provider->DeleteSigningKey(key->GetWrappedKey());
246     delete key;
247   };
248   base::ElapsedTimer key_creation_timer;
249   std::unique_ptr<UnexportableSigningKey, decltype(delete_key)> current_key(
250       provider->GenerateSigningKeySlowly(kAllAlgorithms).release(), delete_key);
251   ReportUmaTpmOperation(TPMOperation::kNewKeyCreation, supported_algo,
252                         key_creation_timer.Elapsed(), current_key != nullptr);
253   if (!current_key) {
254     return;
255   }
256 
257   base::ElapsedTimer wrapped_key_creation_timer;
258   std::unique_ptr<UnexportableSigningKey, decltype(delete_key)> wrapped_key(
259       provider->FromWrappedSigningKeySlowly(current_key->GetWrappedKey())
260           .release(),
261       delete_key);
262   ReportUmaTpmOperation(TPMOperation::kWrappedKeyCreation, supported_algo,
263                         wrapped_key_creation_timer.Elapsed(),
264                         wrapped_key != nullptr);
265 
266   const uint8_t msg[] = {1, 2, 3, 4};
267   base::ElapsedTimer message_signing_timer;
268   std::optional<std::vector<uint8_t>> signed_bytes =
269       current_key->SignSlowly(msg);
270   ReportUmaTpmOperation(TPMOperation::kMessageSigning, supported_algo,
271                         message_signing_timer.Elapsed(),
272                         signed_bytes.has_value());
273   if (!signed_bytes.has_value()) {
274     return;
275   }
276 
277   crypto::SignatureVerifier verifier;
278   bool verify_init =
279       verifier.VerifyInit(current_key->Algorithm(), signed_bytes.value(),
280                           current_key->GetSubjectPublicKeyInfo());
281   if (verify_init) {
282     verifier.VerifyUpdate(msg);
283     bool verify_final = verifier.VerifyFinal();
284     ReportUmaOperationSuccess(TPMOperation::kMessageVerify, supported_algo,
285                               verify_final);
286   } else {
287     ReportUmaOperationSuccess(TPMOperation::kMessageVerify, supported_algo,
288                               verify_init);
289   }
290 }
291 
292 }  // namespace
293 
294 namespace internal {
295 
MeasureTpmOperationsInternalForTesting()296 void MeasureTpmOperationsInternalForTesting() {
297   MeasureTpmOperationsInternal(/*config=*/{});
298 }
299 
300 }  // namespace internal
301 
MaybeMeasureTpmOperations(UnexportableKeyProvider::Config config)302 void MaybeMeasureTpmOperations(UnexportableKeyProvider::Config config) {
303   static BASE_FEATURE(kTpmLatencyMetrics, "TpmLatencyMetrics",
304                       base::FEATURE_ENABLED_BY_DEFAULT);
305   if (base::FeatureList::IsEnabled(kTpmLatencyMetrics)) {
306     base::ThreadPool::PostTask(
307         FROM_HERE,
308         {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
309          base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
310         base::BindOnce(&MeasureTpmOperationsInternal, std::move(config)));
311   }
312 }
313 
314 }  // namespace crypto
315