• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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