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