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