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