1 // Copyright 2021 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "crypto/unexportable_key_win.h"
11
12 #include <string>
13 #include <tuple>
14 #include <vector>
15
16 #include "base/logging.h"
17 #include "base/metrics/histogram_functions.h"
18 #include "base/numerics/checked_math.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/sys_string_conversions.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/threading/scoped_blocking_call.h"
25 #include "base/threading/scoped_thread_priority.h"
26 #include "base/types/expected.h"
27 #include "base/types/optional_util.h"
28 #include "crypto/random.h"
29 #include "crypto/sha2.h"
30 #include "crypto/unexportable_key.h"
31 #include "crypto/unexportable_key_metrics.h"
32 #include "third_party/boringssl/src/include/openssl/bn.h"
33 #include "third_party/boringssl/src/include/openssl/bytestring.h"
34 #include "third_party/boringssl/src/include/openssl/ec.h"
35 #include "third_party/boringssl/src/include/openssl/ec_key.h"
36 #include "third_party/boringssl/src/include/openssl/ecdsa.h"
37 #include "third_party/boringssl/src/include/openssl/evp.h"
38 #include "third_party/boringssl/src/include/openssl/nid.h"
39 #include "third_party/boringssl/src/include/openssl/rsa.h"
40
41 namespace crypto {
42
43 namespace {
44
45 const char kMetricVirtualCreateKeyError[] = "Crypto.TpmError.VirtualCreateKey";
46 const char kMetricVirtualFinalizeKeyError[] =
47 "Crypto.TpmError.VirtualFinalizeKey";
48 const char kMetricVirtualOpenKeyError[] = "Crypto.TpmError.VirtualOpenKey";
49 const char kMetricVirtualOpenStorageError[] =
50 "Crypto.TpmError.VirtualOpenStorage";
51
52 // Logs `status` and `selected_algorithm` to an error histogram capturing that
53 // `operation` failed for a TPM-backed key.
LogTPMOperationError(TPMOperation operation,SECURITY_STATUS status,SignatureVerifier::SignatureAlgorithm selected_algorithm)54 void LogTPMOperationError(
55 TPMOperation operation,
56 SECURITY_STATUS status,
57 SignatureVerifier::SignatureAlgorithm selected_algorithm) {
58 static constexpr char kCreateKeyErrorStatusHistogramFormat[] =
59 "Crypto.TPMOperation.Win.%s%s.Error";
60 base::UmaHistogramSparse(
61 base::StringPrintf(kCreateKeyErrorStatusHistogramFormat,
62 OperationToString(operation).c_str(),
63 AlgorithmToString(selected_algorithm).c_str()),
64 status);
65 }
66
CBBToVector(const CBB * cbb)67 std::vector<uint8_t> CBBToVector(const CBB* cbb) {
68 return std::vector<uint8_t>(CBB_data(cbb), CBB_data(cbb) + CBB_len(cbb));
69 }
70
71 // BCryptAlgorithmFor returns the BCrypt algorithm ID for the given Chromium
72 // signing algorithm.
BCryptAlgorithmFor(SignatureVerifier::SignatureAlgorithm algo)73 std::optional<LPCWSTR> BCryptAlgorithmFor(
74 SignatureVerifier::SignatureAlgorithm algo) {
75 switch (algo) {
76 case SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256:
77 return BCRYPT_RSA_ALGORITHM;
78
79 case SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256:
80 return BCRYPT_ECDSA_P256_ALGORITHM;
81
82 default:
83 return std::nullopt;
84 }
85 }
86
87 // GetBestSupported returns the first element of |acceptable_algorithms| that
88 // |provider| supports, or |nullopt| if there isn't any.
GetBestSupported(NCRYPT_PROV_HANDLE provider,base::span<const SignatureVerifier::SignatureAlgorithm> acceptable_algorithms)89 std::optional<SignatureVerifier::SignatureAlgorithm> GetBestSupported(
90 NCRYPT_PROV_HANDLE provider,
91 base::span<const SignatureVerifier::SignatureAlgorithm>
92 acceptable_algorithms) {
93 for (auto algo : acceptable_algorithms) {
94 std::optional<LPCWSTR> bcrypto_algo_name = BCryptAlgorithmFor(algo);
95 if (!bcrypto_algo_name) {
96 continue;
97 }
98
99 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
100 if (!FAILED(NCryptIsAlgSupported(provider, *bcrypto_algo_name,
101 /*flags=*/0))) {
102 return algo;
103 }
104 }
105
106 return std::nullopt;
107 }
108
109 // GetKeyProperty returns the given NCrypt key property of |key|.
GetKeyProperty(NCRYPT_KEY_HANDLE key,LPCWSTR property)110 std::optional<std::vector<uint8_t>> GetKeyProperty(NCRYPT_KEY_HANDLE key,
111 LPCWSTR property) {
112 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
113 DWORD size;
114 if (FAILED(NCryptGetProperty(key, property, nullptr, 0, &size, 0))) {
115 return std::nullopt;
116 }
117
118 std::vector<uint8_t> ret(size);
119 if (FAILED(
120 NCryptGetProperty(key, property, ret.data(), ret.size(), &size, 0))) {
121 return std::nullopt;
122 }
123 CHECK_EQ(ret.size(), size);
124
125 return ret;
126 }
127
128 // ExportKey returns |key| exported in the given format or nullopt on error.
ExportKey(NCRYPT_KEY_HANDLE key,LPCWSTR format)129 base::expected<std::vector<uint8_t>, SECURITY_STATUS> ExportKey(
130 NCRYPT_KEY_HANDLE key,
131 LPCWSTR format) {
132 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
133 DWORD output_size;
134 SECURITY_STATUS status =
135 NCryptExportKey(key, 0, format, nullptr, nullptr, 0, &output_size, 0);
136 if (FAILED(status)) {
137 return base::unexpected(status);
138 }
139
140 std::vector<uint8_t> output(output_size);
141 status = NCryptExportKey(key, 0, format, nullptr, output.data(),
142 output.size(), &output_size, 0);
143 if (FAILED(status)) {
144 return base::unexpected(status);
145 }
146 CHECK_EQ(output.size(), output_size);
147
148 return output;
149 }
150
GetP256ECDSASPKI(NCRYPT_KEY_HANDLE key)151 std::optional<std::vector<uint8_t>> GetP256ECDSASPKI(NCRYPT_KEY_HANDLE key) {
152 const base::expected<std::vector<uint8_t>, SECURITY_STATUS> pub_key =
153 ExportKey(key, BCRYPT_ECCPUBLIC_BLOB);
154 if (!pub_key.has_value()) {
155 return std::nullopt;
156 }
157
158 // The exported key is a |BCRYPT_ECCKEY_BLOB| followed by the bytes of the
159 // public key itself.
160 // https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_ecckey_blob
161 BCRYPT_ECCKEY_BLOB header;
162 if (pub_key->size() < sizeof(header)) {
163 return std::nullopt;
164 }
165 memcpy(&header, pub_key->data(), sizeof(header));
166 // |cbKey| is documented[1] as "the length, in bytes, of the key". It is
167 // not. For ECDSA public keys it is the length of a field element.
168 if ((header.dwMagic != BCRYPT_ECDSA_PUBLIC_P256_MAGIC &&
169 header.dwMagic != BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC) ||
170 header.cbKey != 256 / 8 ||
171 pub_key->size() - sizeof(BCRYPT_ECCKEY_BLOB) != 64) {
172 return std::nullopt;
173 }
174
175 // Sometimes NCrypt will return a generic dwMagic even when asked for a P-256
176 // key. In that case, do extra validation to make sure that `key` is in fact
177 // a P-256 key.
178 if (header.dwMagic == BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC) {
179 const std::optional<std::vector<uint8_t>> curve_name =
180 GetKeyProperty(key, NCRYPT_ECC_CURVE_NAME_PROPERTY);
181 if (!curve_name) {
182 return std::nullopt;
183 }
184
185 if (curve_name->size() != sizeof(BCRYPT_ECC_CURVE_NISTP256) ||
186 memcmp(curve_name->data(), BCRYPT_ECC_CURVE_NISTP256,
187 sizeof(BCRYPT_ECC_CURVE_NISTP256)) != 0) {
188 return std::nullopt;
189 }
190 }
191
192 uint8_t x962[1 + 32 + 32];
193 x962[0] = POINT_CONVERSION_UNCOMPRESSED;
194 memcpy(&x962[1], pub_key->data() + sizeof(BCRYPT_ECCKEY_BLOB), 64);
195
196 bssl::UniquePtr<EC_GROUP> p256(
197 EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
198 bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
199 if (!EC_POINT_oct2point(p256.get(), point.get(), x962, sizeof(x962),
200 /*ctx=*/nullptr)) {
201 return std::nullopt;
202 }
203 bssl::UniquePtr<EC_KEY> ec_key(
204 EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
205 CHECK(EC_KEY_set_public_key(ec_key.get(), point.get()));
206 bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
207 CHECK(EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()));
208
209 bssl::ScopedCBB cbb;
210 CHECK(CBB_init(cbb.get(), /*initial_capacity=*/128) &&
211 EVP_marshal_public_key(cbb.get(), pkey.get()));
212 return CBBToVector(cbb.get());
213 }
214
GetRSASPKI(NCRYPT_KEY_HANDLE key)215 std::optional<std::vector<uint8_t>> GetRSASPKI(NCRYPT_KEY_HANDLE key) {
216 const base::expected<std::vector<uint8_t>, SECURITY_STATUS> pub_key =
217 ExportKey(key, BCRYPT_RSAPUBLIC_BLOB);
218 if (!pub_key.has_value()) {
219 return std::nullopt;
220 }
221
222 // The exported key is a |BCRYPT_RSAKEY_BLOB| followed by the bytes of the
223 // key itself.
224 // https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_rsakey_blob
225 BCRYPT_RSAKEY_BLOB header;
226 if (pub_key->size() < sizeof(header)) {
227 return std::nullopt;
228 }
229 memcpy(&header, pub_key->data(), sizeof(header));
230 if (header.Magic != static_cast<ULONG>(BCRYPT_RSAPUBLIC_MAGIC)) {
231 return std::nullopt;
232 }
233
234 size_t bytes_needed;
235 if (!base::CheckAdd(sizeof(BCRYPT_RSAKEY_BLOB),
236 base::CheckAdd(header.cbPublicExp, header.cbModulus))
237 .AssignIfValid(&bytes_needed) ||
238 pub_key->size() < bytes_needed) {
239 return std::nullopt;
240 }
241
242 bssl::UniquePtr<BIGNUM> e(
243 BN_bin2bn(&pub_key->data()[sizeof(BCRYPT_RSAKEY_BLOB)],
244 header.cbPublicExp, nullptr));
245 bssl::UniquePtr<BIGNUM> n(BN_bin2bn(
246 &pub_key->data()[sizeof(BCRYPT_RSAKEY_BLOB) + header.cbPublicExp],
247 header.cbModulus, nullptr));
248
249 bssl::UniquePtr<RSA> rsa(RSA_new());
250 CHECK(RSA_set0_key(rsa.get(), n.release(), e.release(), nullptr));
251 bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
252 CHECK(EVP_PKEY_set1_RSA(pkey.get(), rsa.get()));
253
254 bssl::ScopedCBB cbb;
255 CHECK(CBB_init(cbb.get(), /*initial_capacity=*/384) &&
256 EVP_marshal_public_key(cbb.get(), pkey.get()));
257 return CBBToVector(cbb.get());
258 }
259
SignECDSA(NCRYPT_KEY_HANDLE key,base::span<const uint8_t> data)260 base::expected<std::vector<uint8_t>, SECURITY_STATUS> SignECDSA(
261 NCRYPT_KEY_HANDLE key,
262 base::span<const uint8_t> data) {
263 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
264 base::BlockingType::WILL_BLOCK);
265
266 std::array<uint8_t, kSHA256Length> digest = SHA256Hash(data);
267 // The signature is written as a pair of big-endian field elements for P-256
268 // ECDSA.
269 std::vector<uint8_t> sig(64);
270 DWORD sig_size;
271 {
272 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
273 SECURITY_STATUS status =
274 NCryptSignHash(key, nullptr, digest.data(), digest.size(), sig.data(),
275 sig.size(), &sig_size, NCRYPT_SILENT_FLAG);
276 if (FAILED(status)) {
277 return base::unexpected(status);
278 }
279 }
280 CHECK_EQ(sig.size(), sig_size);
281
282 bssl::UniquePtr<BIGNUM> r(BN_bin2bn(sig.data(), 32, nullptr));
283 bssl::UniquePtr<BIGNUM> s(BN_bin2bn(sig.data() + 32, 32, nullptr));
284 ECDSA_SIG sig_st;
285 sig_st.r = r.get();
286 sig_st.s = s.get();
287
288 bssl::ScopedCBB cbb;
289 CHECK(CBB_init(cbb.get(), /*initial_capacity=*/72) &&
290 ECDSA_SIG_marshal(cbb.get(), &sig_st));
291 return CBBToVector(cbb.get());
292 }
293
SignRSA(NCRYPT_KEY_HANDLE key,base::span<const uint8_t> data)294 base::expected<std::vector<uint8_t>, SECURITY_STATUS> SignRSA(
295 NCRYPT_KEY_HANDLE key,
296 base::span<const uint8_t> data) {
297 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
298 base::BlockingType::WILL_BLOCK);
299
300 std::array<uint8_t, kSHA256Length> digest = SHA256Hash(data);
301 BCRYPT_PKCS1_PADDING_INFO padding_info = {0};
302 padding_info.pszAlgId = NCRYPT_SHA256_ALGORITHM;
303
304 DWORD sig_size;
305 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
306 SECURITY_STATUS status =
307 NCryptSignHash(key, &padding_info, digest.data(), digest.size(), nullptr,
308 0, &sig_size, NCRYPT_SILENT_FLAG | BCRYPT_PAD_PKCS1);
309 if (FAILED(status)) {
310 return base::unexpected(status);
311 }
312
313 std::vector<uint8_t> sig(sig_size);
314 status = NCryptSignHash(key, &padding_info, digest.data(), digest.size(),
315 sig.data(), sig.size(), &sig_size,
316 NCRYPT_SILENT_FLAG | BCRYPT_PAD_PKCS1);
317 if (FAILED(status)) {
318 return base::unexpected(status);
319 }
320 CHECK_EQ(sig.size(), sig_size);
321
322 return sig;
323 }
324
325 // ECDSAKey wraps a TPM-stored P-256 ECDSA key.
326 class ECDSAKey : public UnexportableSigningKey {
327 public:
ECDSAKey(ScopedNCryptKey key,std::vector<uint8_t> wrapped,std::vector<uint8_t> spki)328 ECDSAKey(ScopedNCryptKey key,
329 std::vector<uint8_t> wrapped,
330 std::vector<uint8_t> spki)
331 : key_(std::move(key)),
332 wrapped_(std::move(wrapped)),
333 spki_(std::move(spki)) {}
334
Algorithm() const335 SignatureVerifier::SignatureAlgorithm Algorithm() const override {
336 return SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256;
337 }
338
GetSubjectPublicKeyInfo() const339 std::vector<uint8_t> GetSubjectPublicKeyInfo() const override {
340 return spki_;
341 }
342
GetWrappedKey() const343 std::vector<uint8_t> GetWrappedKey() const override { return wrapped_; }
344
SignSlowly(base::span<const uint8_t> data)345 std::optional<std::vector<uint8_t>> SignSlowly(
346 base::span<const uint8_t> data) override {
347 base::expected<std::vector<uint8_t>, SECURITY_STATUS> signature =
348 SignECDSA(key_.get(), data);
349 if (!signature.has_value()) {
350 LogTPMOperationError(TPMOperation::kMessageSigning, signature.error(),
351 Algorithm());
352 }
353
354 return base::OptionalFromExpected(signature);
355 }
356
IsHardwareBacked() const357 bool IsHardwareBacked() const override { return true; }
358
359 private:
360 ScopedNCryptKey key_;
361 const std::vector<uint8_t> wrapped_;
362 const std::vector<uint8_t> spki_;
363 };
364
365 // RSAKey wraps a TPM-stored RSA key.
366 class RSAKey : public UnexportableSigningKey {
367 public:
RSAKey(ScopedNCryptKey key,std::vector<uint8_t> wrapped,std::vector<uint8_t> spki)368 RSAKey(ScopedNCryptKey key,
369 std::vector<uint8_t> wrapped,
370 std::vector<uint8_t> spki)
371 : key_(std::move(key)),
372 wrapped_(std::move(wrapped)),
373 spki_(std::move(spki)) {}
374
Algorithm() const375 SignatureVerifier::SignatureAlgorithm Algorithm() const override {
376 return SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256;
377 }
378
GetSubjectPublicKeyInfo() const379 std::vector<uint8_t> GetSubjectPublicKeyInfo() const override {
380 return spki_;
381 }
382
GetWrappedKey() const383 std::vector<uint8_t> GetWrappedKey() const override { return wrapped_; }
384
SignSlowly(base::span<const uint8_t> data)385 std::optional<std::vector<uint8_t>> SignSlowly(
386 base::span<const uint8_t> data) override {
387 base::expected<std::vector<uint8_t>, SECURITY_STATUS> signature =
388 SignRSA(key_.get(), data);
389 if (!signature.has_value()) {
390 LogTPMOperationError(TPMOperation::kMessageSigning, signature.error(),
391 Algorithm());
392 }
393
394 return base::OptionalFromExpected(signature);
395 }
396
IsHardwareBacked() const397 bool IsHardwareBacked() const override { return true; }
398
399 private:
400 ScopedNCryptKey key_;
401 const std::vector<uint8_t> wrapped_;
402 const std::vector<uint8_t> spki_;
403 };
404
405 // UnexportableKeyProviderWin uses NCrypt and the Platform Crypto
406 // Provider to expose TPM-backed keys on Windows.
407 class UnexportableKeyProviderWin : public UnexportableKeyProvider {
408 public:
409 ~UnexportableKeyProviderWin() override = default;
410
SelectAlgorithm(base::span<const SignatureVerifier::SignatureAlgorithm> acceptable_algorithms)411 std::optional<SignatureVerifier::SignatureAlgorithm> SelectAlgorithm(
412 base::span<const SignatureVerifier::SignatureAlgorithm>
413 acceptable_algorithms) override {
414 ScopedNCryptProvider provider;
415 {
416 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
417 if (FAILED(NCryptOpenStorageProvider(
418 ScopedNCryptProvider::Receiver(provider).get(),
419 MS_PLATFORM_CRYPTO_PROVIDER, /*flags=*/0))) {
420 return std::nullopt;
421 }
422 }
423
424 return GetBestSupported(provider.get(), acceptable_algorithms);
425 }
426
GenerateSigningKeySlowly(base::span<const SignatureVerifier::SignatureAlgorithm> acceptable_algorithms)427 std::unique_ptr<UnexportableSigningKey> GenerateSigningKeySlowly(
428 base::span<const SignatureVerifier::SignatureAlgorithm>
429 acceptable_algorithms) override {
430 base::ScopedBlockingCall scoped_blocking_call(
431 FROM_HERE, base::BlockingType::WILL_BLOCK);
432
433 ScopedNCryptProvider provider;
434 {
435 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
436 if (FAILED(NCryptOpenStorageProvider(
437 ScopedNCryptProvider::Receiver(provider).get(),
438 MS_PLATFORM_CRYPTO_PROVIDER, /*flags=*/0))) {
439 return nullptr;
440 }
441 }
442
443 std::optional<SignatureVerifier::SignatureAlgorithm> algo =
444 GetBestSupported(provider.get(), acceptable_algorithms);
445 if (!algo) {
446 return nullptr;
447 }
448
449 ScopedNCryptKey key;
450 {
451 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
452 // An empty key name stops the key being persisted to disk.
453 SECURITY_STATUS creation_status = NCryptCreatePersistedKey(
454 provider.get(), ScopedNCryptKey::Receiver(key).get(),
455 BCryptAlgorithmFor(*algo).value(), /*pszKeyName=*/nullptr,
456 /*dwLegacyKeySpec=*/0, /*dwFlags=*/0);
457 if (FAILED(creation_status)) {
458 LogTPMOperationError(TPMOperation::kNewKeyCreation, creation_status,
459 algo.value());
460 return nullptr;
461 }
462
463 if (FAILED(NCryptFinalizeKey(key.get(), NCRYPT_SILENT_FLAG))) {
464 return nullptr;
465 }
466 }
467
468 const base::expected<std::vector<uint8_t>, SECURITY_STATUS> wrapped_key =
469 ExportKey(key.get(), BCRYPT_OPAQUE_KEY_BLOB);
470 if (!wrapped_key.has_value()) {
471 LogTPMOperationError(TPMOperation::kWrappedKeyCreation,
472 wrapped_key.error(), algo.value());
473 return nullptr;
474 }
475
476 std::optional<std::vector<uint8_t>> spki;
477 switch (*algo) {
478 case SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256:
479 spki = GetP256ECDSASPKI(key.get());
480 if (!spki) {
481 return nullptr;
482 }
483 return std::make_unique<ECDSAKey>(
484 std::move(key), std::move(*wrapped_key), std::move(spki.value()));
485 case SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256:
486 spki = GetRSASPKI(key.get());
487 if (!spki) {
488 return nullptr;
489 }
490 return std::make_unique<RSAKey>(std::move(key), std::move(*wrapped_key),
491 std::move(spki.value()));
492 default:
493 return nullptr;
494 }
495 }
496
FromWrappedSigningKeySlowly(base::span<const uint8_t> wrapped)497 std::unique_ptr<UnexportableSigningKey> FromWrappedSigningKeySlowly(
498 base::span<const uint8_t> wrapped) override {
499 base::ScopedBlockingCall scoped_blocking_call(
500 FROM_HERE, base::BlockingType::WILL_BLOCK);
501
502 ScopedNCryptProvider provider;
503 ScopedNCryptKey key;
504 if (!LoadWrappedTPMKey(wrapped, provider, key)) {
505 return nullptr;
506 }
507
508 const std::optional<std::vector<uint8_t>> algo_bytes =
509 GetKeyProperty(key.get(), NCRYPT_ALGORITHM_PROPERTY);
510 if (!algo_bytes) {
511 return nullptr;
512 }
513
514 // The documentation suggests that |NCRYPT_ALGORITHM_PROPERTY| should return
515 // the original algorithm, i.e. |BCRYPT_ECDSA_P256_ALGORITHM| for ECDSA. But
516 // it actually returns just "ECDSA" for that case.
517 static const wchar_t kECDSA[] = L"ECDSA";
518 static const wchar_t kRSA[] = BCRYPT_RSA_ALGORITHM;
519
520 std::optional<std::vector<uint8_t>> spki;
521 if (algo_bytes->size() == sizeof(kECDSA) &&
522 memcmp(algo_bytes->data(), kECDSA, sizeof(kECDSA)) == 0) {
523 spki = GetP256ECDSASPKI(key.get());
524 if (!spki) {
525 return nullptr;
526 }
527 return std::make_unique<ECDSAKey>(
528 std::move(key), std::vector<uint8_t>(wrapped.begin(), wrapped.end()),
529 std::move(spki.value()));
530 } else if (algo_bytes->size() == sizeof(kRSA) &&
531 memcmp(algo_bytes->data(), kRSA, sizeof(kRSA)) == 0) {
532 spki = GetRSASPKI(key.get());
533 if (!spki) {
534 return nullptr;
535 }
536 return std::make_unique<RSAKey>(
537 std::move(key), std::vector<uint8_t>(wrapped.begin(), wrapped.end()),
538 std::move(spki.value()));
539 }
540
541 return nullptr;
542 }
543
DeleteSigningKeySlowly(base::span<const uint8_t> wrapped)544 bool DeleteSigningKeySlowly(base::span<const uint8_t> wrapped) override {
545 // Unexportable keys are stateless on Windows.
546 return true;
547 }
548 };
549
550 // ECDSASoftwareKey wraps a Credential Guard stored P-256 ECDSA key.
551 class ECDSASoftwareKey : public VirtualUnexportableSigningKey {
552 public:
ECDSASoftwareKey(ScopedNCryptKey key,std::string name,std::vector<uint8_t> spki)553 ECDSASoftwareKey(ScopedNCryptKey key,
554 std::string name,
555 std::vector<uint8_t> spki)
556 : key_(std::move(key)), name_(std::move(name)), spki_(std::move(spki)) {}
557
Algorithm() const558 SignatureVerifier::SignatureAlgorithm Algorithm() const override {
559 return SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256;
560 }
561
GetSubjectPublicKeyInfo() const562 std::vector<uint8_t> GetSubjectPublicKeyInfo() const override {
563 return spki_;
564 }
565
GetKeyName() const566 std::string GetKeyName() const override { return name_; }
567
Sign(base::span<const uint8_t> data)568 std::optional<std::vector<uint8_t>> Sign(
569 base::span<const uint8_t> data) override {
570 if (!key_.is_valid()) {
571 return std::nullopt;
572 }
573
574 return base::OptionalFromExpected(SignECDSA(key_.get(), data));
575 }
576
DeleteKey()577 void DeleteKey() override {
578 if (!key_.is_valid()) {
579 return;
580 }
581
582 // If key deletion succeeds, NCryptDeleteKey frees the key. To avoid double
583 // free, we need to release the key from the ScopedNCryptKey RAII object.
584 // Key deletion can fail in circumstances which are not under the
585 // application's control. For these cases, ScopedNCrypt key should free the
586 // key.
587 if (NCryptDeleteKey(key_.get(), NCRYPT_SILENT_FLAG) == ERROR_SUCCESS) {
588 static_cast<void>(key_.release());
589 }
590 }
591
592 private:
593 ScopedNCryptKey key_;
594 const std::string name_;
595 const std::vector<uint8_t> spki_;
596 };
597
598 // RSASoftwareKey wraps a Credential Guard stored RSA key.
599 class RSASoftwareKey : public VirtualUnexportableSigningKey {
600 public:
RSASoftwareKey(ScopedNCryptKey key,std::string name,std::vector<uint8_t> spki)601 RSASoftwareKey(ScopedNCryptKey key,
602 std::string name,
603 std::vector<uint8_t> spki)
604 : key_(std::move(key)), name_(std::move(name)), spki_(std::move(spki)) {}
605
Algorithm() const606 SignatureVerifier::SignatureAlgorithm Algorithm() const override {
607 return SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256;
608 }
609
GetSubjectPublicKeyInfo() const610 std::vector<uint8_t> GetSubjectPublicKeyInfo() const override {
611 return spki_;
612 }
613
GetKeyName() const614 std::string GetKeyName() const override { return name_; }
615
Sign(base::span<const uint8_t> data)616 std::optional<std::vector<uint8_t>> Sign(
617 base::span<const uint8_t> data) override {
618 if (!key_.is_valid()) {
619 return std::nullopt;
620 }
621
622 return base::OptionalFromExpected(SignRSA(key_.get(), data));
623 }
624
DeleteKey()625 void DeleteKey() override {
626 if (!key_.is_valid()) {
627 return;
628 }
629
630 // If key deletion succeeds, NCryptDeleteKey frees the key. To avoid double
631 // free, we need to release the key from the ScopedNCryptKey RAII object.
632 // Key deletion can fail in circumstances which are not under the
633 // application's control. For these cases, ScopedNCrypt key should free the
634 // key.
635 if (NCryptDeleteKey(key_.get(), NCRYPT_SILENT_FLAG) == ERROR_SUCCESS) {
636 static_cast<void>(key_.release());
637 }
638 }
639
640 private:
641 ScopedNCryptKey key_;
642 std::string name_;
643 const std::vector<uint8_t> spki_;
644 };
645
646 // UnexportableKeyProviderWin uses NCrypt and the Platform Crypto
647 // Provider to expose Credential Guard backed keys on Windows.
648 class VirtualUnexportableKeyProviderWin
649 : public VirtualUnexportableKeyProvider {
650 public:
651 ~VirtualUnexportableKeyProviderWin() override = default;
652
SelectAlgorithm(base::span<const SignatureVerifier::SignatureAlgorithm> acceptable_algorithms)653 std::optional<SignatureVerifier::SignatureAlgorithm> SelectAlgorithm(
654 base::span<const SignatureVerifier::SignatureAlgorithm>
655 acceptable_algorithms) override {
656 ScopedNCryptProvider provider;
657 {
658 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
659 SECURITY_STATUS status = NCryptOpenStorageProvider(
660 ScopedNCryptProvider::Receiver(provider).get(),
661 MS_KEY_STORAGE_PROVIDER, /*dwFlags=*/0);
662 if (FAILED(status)) {
663 base::UmaHistogramSparse(kMetricVirtualOpenStorageError, status);
664 return std::nullopt;
665 }
666 }
667
668 return GetBestSupported(provider.get(), acceptable_algorithms);
669 }
670
GenerateSigningKey(base::span<const SignatureVerifier::SignatureAlgorithm> acceptable_algorithms,std::string name)671 std::unique_ptr<VirtualUnexportableSigningKey> GenerateSigningKey(
672 base::span<const SignatureVerifier::SignatureAlgorithm>
673 acceptable_algorithms,
674 std::string name) override {
675 base::ScopedBlockingCall scoped_blocking_call(
676 FROM_HERE, base::BlockingType::WILL_BLOCK);
677
678 ScopedNCryptProvider provider;
679 {
680 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
681 SECURITY_STATUS status = NCryptOpenStorageProvider(
682 ScopedNCryptProvider::Receiver(provider).get(),
683 MS_KEY_STORAGE_PROVIDER, /*dwFlags=*/0);
684 if (FAILED(status)) {
685 base::UmaHistogramSparse(kMetricVirtualOpenStorageError, status);
686 return nullptr;
687 }
688 }
689
690 std::optional<SignatureVerifier::SignatureAlgorithm> algo =
691 GetBestSupported(provider.get(), acceptable_algorithms);
692 if (!algo) {
693 return nullptr;
694 }
695
696 ScopedNCryptKey key;
697 {
698 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
699 // An empty key name stops the key being persisted to disk.
700 SECURITY_STATUS status = NCryptCreatePersistedKey(
701 provider.get(), ScopedNCryptKey::Receiver(key).get(),
702 BCryptAlgorithmFor(*algo).value(), base::SysUTF8ToWide(name).c_str(),
703 /*dwLegacyKeySpec=*/0,
704 /*dwFlags=*/NCRYPT_USE_VIRTUAL_ISOLATION_FLAG);
705 if (FAILED(status)) {
706 base::UmaHistogramSparse(kMetricVirtualCreateKeyError, status);
707 return nullptr;
708 }
709
710 status = NCryptFinalizeKey(
711 key.get(), NCRYPT_PROTECT_TO_LOCAL_SYSTEM | NCRYPT_SILENT_FLAG);
712 if (FAILED(status)) {
713 base::UmaHistogramSparse(kMetricVirtualFinalizeKeyError, status);
714 return nullptr;
715 }
716 }
717
718 std::optional<std::vector<uint8_t>> spki;
719 switch (*algo) {
720 case SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256:
721 spki = GetP256ECDSASPKI(key.get());
722 if (!spki) {
723 return nullptr;
724 }
725 return std::make_unique<ECDSASoftwareKey>(std::move(key), name,
726 std::move(spki.value()));
727 case SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256:
728 spki = GetRSASPKI(key.get());
729 if (!spki) {
730 return nullptr;
731 }
732 return std::make_unique<RSASoftwareKey>(std::move(key), name,
733 std::move(spki.value()));
734 default:
735 return nullptr;
736 }
737 }
738
FromKeyName(std::string name)739 std::unique_ptr<VirtualUnexportableSigningKey> FromKeyName(
740 std::string name) override {
741 base::ScopedBlockingCall scoped_blocking_call(
742 FROM_HERE, base::BlockingType::WILL_BLOCK);
743
744 ScopedNCryptProvider provider;
745 ScopedNCryptKey key;
746 {
747 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
748 SECURITY_STATUS status = NCryptOpenStorageProvider(
749 ScopedNCryptProvider::Receiver(provider).get(),
750 MS_KEY_STORAGE_PROVIDER, /*dwFlags=*/0);
751 if (FAILED(status)) {
752 base::UmaHistogramSparse(kMetricVirtualOpenStorageError, status);
753 return nullptr;
754 }
755
756 status = NCryptOpenKey(
757 provider.get(), ScopedNCryptKey::Receiver(key).get(),
758 base::SysUTF8ToWide(name).c_str(), /*dwLegacyKeySpec=*/0,
759 /*dwFlags*/ 0);
760 if (FAILED(status)) {
761 base::UmaHistogramSparse(kMetricVirtualOpenKeyError, status);
762 return nullptr;
763 }
764 }
765
766 const std::optional<std::vector<uint8_t>> algo_bytes =
767 GetKeyProperty(key.get(), NCRYPT_ALGORITHM_PROPERTY);
768
769 // This is the expected behavior, but note it is different from
770 // TPM backed keys.
771 static const wchar_t kECDSA[] = BCRYPT_ECDSA_P256_ALGORITHM;
772 static const wchar_t kRSA[] = BCRYPT_RSA_ALGORITHM;
773
774 std::optional<std::vector<uint8_t>> spki;
775 if (algo_bytes->size() == sizeof(kECDSA) &&
776 memcmp(algo_bytes->data(), kECDSA, sizeof(kECDSA)) == 0) {
777 spki = GetP256ECDSASPKI(key.get());
778 if (!spki) {
779 return nullptr;
780 }
781 return std::make_unique<ECDSASoftwareKey>(std::move(key), name,
782 std::move(spki.value()));
783 } else if (algo_bytes->size() == sizeof(kRSA) &&
784 memcmp(algo_bytes->data(), kRSA, sizeof(kRSA)) == 0) {
785 spki = GetRSASPKI(key.get());
786 if (!spki) {
787 return nullptr;
788 }
789 return std::make_unique<RSASoftwareKey>(std::move(key), name,
790 std::move(spki.value()));
791 }
792
793 return nullptr;
794 }
795 };
796
797 } // namespace
798
LoadWrappedTPMKey(base::span<const uint8_t> wrapped,ScopedNCryptProvider & provider,ScopedNCryptKey & key)799 bool LoadWrappedTPMKey(base::span<const uint8_t> wrapped,
800 ScopedNCryptProvider& provider,
801 ScopedNCryptKey& key) {
802 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
803 if (FAILED(NCryptOpenStorageProvider(
804 ScopedNCryptProvider::Receiver(provider).get(),
805 MS_PLATFORM_CRYPTO_PROVIDER,
806 /*flags=*/0))) {
807 return false;
808 }
809
810 if (FAILED(NCryptImportKey(
811 provider.get(), /*hImportKey=*/NULL, BCRYPT_OPAQUE_KEY_BLOB,
812 /*pParameterList=*/nullptr, ScopedNCryptKey::Receiver(key).get(),
813 const_cast<PBYTE>(wrapped.data()), wrapped.size(),
814 /*dwFlags=*/NCRYPT_SILENT_FLAG))) {
815 return false;
816 }
817 return true;
818 }
819
GetUnexportableKeyProviderWin()820 std::unique_ptr<UnexportableKeyProvider> GetUnexportableKeyProviderWin() {
821 return std::make_unique<UnexportableKeyProviderWin>();
822 }
823
824 std::unique_ptr<VirtualUnexportableKeyProvider>
GetVirtualUnexportableKeyProviderWin()825 GetVirtualUnexportableKeyProviderWin() {
826 return std::make_unique<VirtualUnexportableKeyProviderWin>();
827 }
828
829 } // namespace crypto
830