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