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