• 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 #include "crypto/unexportable_key.h"
6 
7 #include <optional>
8 #include <tuple>
9 
10 #include "base/logging.h"
11 #include "base/time/time.h"
12 #include "crypto/scoped_mock_unexportable_key_provider.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 
15 #if BUILDFLAG(IS_MAC)
16 #include "crypto/scoped_fake_apple_keychain_v2.h"
17 #endif  // BUILDFLAG(IS_MAC)
18 
19 namespace {
20 
21 const crypto::SignatureVerifier::SignatureAlgorithm kAllAlgorithms[] = {
22     crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256,
23     crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256,
24 };
25 
26 #if BUILDFLAG(IS_MAC)
27 constexpr char kTestKeychainAccessGroup[] = "test-keychain-access-group";
28 #endif  // BUILDFLAG(IS_MAC)
29 
30 class UnexportableKeySigningTest
31     : public testing::TestWithParam<
32           std::tuple<crypto::SignatureVerifier::SignatureAlgorithm, bool>> {
33  private:
34 #if BUILDFLAG(IS_MAC)
35   crypto::ScopedFakeAppleKeychainV2 scoped_fake_apple_keychain_{
36       kTestKeychainAccessGroup};
37 #endif  // BUILDFLAG(IS_MAC)
38 };
39 
40 INSTANTIATE_TEST_SUITE_P(All,
41                          UnexportableKeySigningTest,
42                          testing::Combine(testing::ValuesIn(kAllAlgorithms),
43                                           testing::Bool()));
44 
TEST_P(UnexportableKeySigningTest,RoundTrip)45 TEST_P(UnexportableKeySigningTest, RoundTrip) {
46   const crypto::SignatureVerifier::SignatureAlgorithm algo =
47       std::get<0>(GetParam());
48   const bool mock_enabled = std::get<1>(GetParam());
49 
50   switch (algo) {
51     case crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256:
52       LOG(INFO) << "ECDSA P-256, mock=" << mock_enabled;
53       break;
54     case crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256:
55       LOG(INFO) << "RSA, mock=" << mock_enabled;
56       break;
57     default:
58       ASSERT_TRUE(false);
59   }
60 
61   SCOPED_TRACE(static_cast<int>(algo));
62   SCOPED_TRACE(mock_enabled);
63 
64   std::optional<crypto::ScopedMockUnexportableKeyProvider> mock;
65   if (mock_enabled) {
66     mock.emplace();
67   }
68 
69   const crypto::SignatureVerifier::SignatureAlgorithm algorithms[] = {algo};
70 
71   crypto::UnexportableKeyProvider::Config config{
72 #if BUILDFLAG(IS_MAC)
73       .keychain_access_group = kTestKeychainAccessGroup
74 #endif  // BUILDLFAG(IS_MAC)
75   };
76   std::unique_ptr<crypto::UnexportableKeyProvider> provider =
77       crypto::GetUnexportableKeyProvider(std::move(config));
78   if (!provider) {
79     LOG(INFO) << "Skipping test because of lack of hardware support.";
80     return;
81   }
82 
83   if (!provider->SelectAlgorithm(algorithms)) {
84     LOG(INFO) << "Skipping test because of lack of support for this key type.";
85     return;
86   }
87 
88   const base::TimeTicks generate_start = base::TimeTicks::Now();
89   std::unique_ptr<crypto::UnexportableSigningKey> key =
90       provider->GenerateSigningKeySlowly(algorithms);
91   if (algo == crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256) {
92     if (!key) {
93       GTEST_SKIP()
94           << "Workaround for https://issues.chromium.org/issues/41494935";
95     }
96   }
97 
98   ASSERT_TRUE(key);
99   LOG(INFO) << "Generation took " << (base::TimeTicks::Now() - generate_start);
100 
101   ASSERT_EQ(key->Algorithm(), algo);
102   const std::vector<uint8_t> wrapped = key->GetWrappedKey();
103   const std::vector<uint8_t> spki = key->GetSubjectPublicKeyInfo();
104   const uint8_t msg[] = {1, 2, 3, 4};
105 
106   const base::TimeTicks sign_start = base::TimeTicks::Now();
107   const std::optional<std::vector<uint8_t>> sig = key->SignSlowly(msg);
108   LOG(INFO) << "Signing took " << (base::TimeTicks::Now() - sign_start);
109   ASSERT_TRUE(sig);
110 
111   crypto::SignatureVerifier verifier;
112   ASSERT_TRUE(verifier.VerifyInit(algo, *sig, spki));
113   verifier.VerifyUpdate(msg);
114   ASSERT_TRUE(verifier.VerifyFinal());
115 
116   const base::TimeTicks import2_start = base::TimeTicks::Now();
117   std::unique_ptr<crypto::UnexportableSigningKey> key2 =
118       provider->FromWrappedSigningKeySlowly(wrapped);
119   ASSERT_TRUE(key2);
120   LOG(INFO) << "Import took " << (base::TimeTicks::Now() - import2_start);
121 
122   const base::TimeTicks sign2_start = base::TimeTicks::Now();
123   const std::optional<std::vector<uint8_t>> sig2 = key->SignSlowly(msg);
124   LOG(INFO) << "Signing took " << (base::TimeTicks::Now() - sign2_start);
125   ASSERT_TRUE(sig2);
126 
127   crypto::SignatureVerifier verifier2;
128   ASSERT_TRUE(verifier2.VerifyInit(algo, *sig2, spki));
129   verifier2.VerifyUpdate(msg);
130   ASSERT_TRUE(verifier2.VerifyFinal());
131 
132   EXPECT_TRUE(provider->DeleteSigningKeySlowly(wrapped));
133 }
134 }  // namespace
135