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