xref: /aosp_15_r20/external/cronet/crypto/unexportable_key_win.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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