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