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