xref: /aosp_15_r20/external/tink/cc/hybrid/hybrid_decrypt_wrapper_test.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 #include "tink/hybrid/hybrid_decrypt_wrapper.h"
18 
19 #include <memory>
20 #include <string>
21 #include <utility>
22 
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "absl/strings/str_cat.h"
26 #include "tink/hybrid/failing_hybrid.h"
27 #include "tink/hybrid_decrypt.h"
28 #include "tink/internal/registry_impl.h"
29 #include "tink/monitoring/monitoring.h"
30 #include "tink/monitoring/monitoring_client_mocks.h"
31 #include "tink/primitive_set.h"
32 #include "tink/util/status.h"
33 #include "tink/util/test_matchers.h"
34 #include "tink/util/test_util.h"
35 #include "proto/tink.pb.h"
36 
37 using ::crypto::tink::test::DummyHybridDecrypt;
38 using ::crypto::tink::test::DummyHybridEncrypt;
39 using ::crypto::tink::test::IsOk;
40 using ::crypto::tink::test::IsOkAndHolds;
41 using ::crypto::tink::test::StatusIs;
42 using ::google::crypto::tink::KeysetInfo;
43 using ::google::crypto::tink::KeyStatusType;
44 using ::google::crypto::tink::OutputPrefixType;
45 using ::testing::_;
46 using ::testing::ByMove;
47 using ::testing::IsNull;
48 using ::testing::NiceMock;
49 using ::testing::Not;
50 using ::testing::NotNull;
51 using ::testing::Return;
52 using ::testing::Test;
53 
54 namespace crypto {
55 namespace tink {
56 namespace {
57 
58 class HybridDecryptSetWrapperTest : public ::testing::Test {
59  protected:
SetUp()60   void SetUp() override {
61   }
TearDown()62   void TearDown() override {
63   }
64 };
65 
TEST_F(HybridDecryptSetWrapperTest,Basic)66 TEST_F(HybridDecryptSetWrapperTest, Basic) {
67   { // hybrid_decrypt_set is nullptr.
68     auto hybrid_decrypt_result =
69         HybridDecryptWrapper().Wrap(nullptr);
70     EXPECT_FALSE(hybrid_decrypt_result.ok());
71     EXPECT_EQ(absl::StatusCode::kInternal,
72               hybrid_decrypt_result.status().code());
73     EXPECT_PRED_FORMAT2(testing::IsSubstring, "non-NULL",
74                         std::string(hybrid_decrypt_result.status().message()));
75   }
76 
77   { // hybrid_decrypt_set has no primary primitive.
78     std::unique_ptr<PrimitiveSet<HybridDecrypt>>
79         hybrid_decrypt_set(new PrimitiveSet<HybridDecrypt>());
80     auto hybrid_decrypt_result = HybridDecryptWrapper().Wrap(
81         std::move(hybrid_decrypt_set));
82     EXPECT_FALSE(hybrid_decrypt_result.ok());
83     EXPECT_EQ(absl::StatusCode::kInvalidArgument,
84         hybrid_decrypt_result.status().code());
85     EXPECT_PRED_FORMAT2(testing::IsSubstring, "no primary",
86                         std::string(hybrid_decrypt_result.status().message()));
87   }
88 
89   { // Correct hybrid_decrypt_set;
90     KeysetInfo::KeyInfo* key;
91     KeysetInfo keyset;
92 
93     uint32_t key_id_0 = 1234543;
94     key = keyset.add_key_info();
95     key->set_output_prefix_type(OutputPrefixType::RAW);
96     key->set_key_id(key_id_0);
97     key->set_status(KeyStatusType::ENABLED);
98 
99     uint32_t key_id_1 = 726329;
100     key = keyset.add_key_info();
101     key->set_output_prefix_type(OutputPrefixType::LEGACY);
102     key->set_key_id(key_id_1);
103     key->set_status(KeyStatusType::ENABLED);
104 
105     uint32_t key_id_2 = 7213743;
106     key = keyset.add_key_info();
107     key->set_output_prefix_type(OutputPrefixType::TINK);
108     key->set_key_id(key_id_2);
109     key->set_status(KeyStatusType::ENABLED);
110 
111     std::string hybrid_name_0 = "hybrid_0";
112     std::string hybrid_name_1 = "hybrid_1";
113     std::string hybrid_name_2 = "hybrid_2";
114     std::unique_ptr<PrimitiveSet<HybridDecrypt>> hybrid_decrypt_set(
115         new PrimitiveSet<HybridDecrypt>());
116     std::unique_ptr<HybridDecrypt> hybrid_decrypt(
117         new DummyHybridDecrypt(hybrid_name_0));
118     auto entry_result = hybrid_decrypt_set->AddPrimitive(
119         std::move(hybrid_decrypt), keyset.key_info(0));
120     ASSERT_TRUE(entry_result.ok());
121     hybrid_decrypt.reset(new DummyHybridDecrypt(hybrid_name_1));
122     entry_result = hybrid_decrypt_set->AddPrimitive(std::move(hybrid_decrypt),
123                                                     keyset.key_info(1));
124     ASSERT_TRUE(entry_result.ok());
125     std::string prefix_id_1 = entry_result.value()->get_identifier();
126     hybrid_decrypt.reset(new DummyHybridDecrypt(hybrid_name_2));
127     entry_result = hybrid_decrypt_set->AddPrimitive(std::move(hybrid_decrypt),
128                                                     keyset.key_info(2));
129     ASSERT_TRUE(entry_result.ok());
130     // The last key is the primary.
131     ASSERT_THAT(hybrid_decrypt_set->set_primary(entry_result.value()), IsOk());
132 
133     // Wrap hybrid_decrypt_set and test the resulting HybridDecrypt.
134     auto hybrid_decrypt_result = HybridDecryptWrapper().Wrap(
135         std::move(hybrid_decrypt_set));
136     EXPECT_TRUE(hybrid_decrypt_result.ok()) << hybrid_decrypt_result.status();
137     hybrid_decrypt = std::move(hybrid_decrypt_result.value());
138     std::string plaintext = "some_plaintext";
139     std::string context_info = "some_context";
140 
141     {  // RAW key
142       std::string ciphertext = DummyHybridEncrypt(hybrid_name_0)
143                                    .Encrypt(plaintext, context_info)
144                                    .value();
145       auto decrypt_result = hybrid_decrypt->Decrypt(ciphertext, context_info);
146       EXPECT_TRUE(decrypt_result.ok()) << decrypt_result.status();
147       EXPECT_EQ(plaintext, decrypt_result.value());
148     }
149 
150     {  // No ciphertext prefix.
151       std::string ciphertext = plaintext + hybrid_name_1;
152       auto decrypt_result = hybrid_decrypt->Decrypt(ciphertext, context_info);
153       EXPECT_FALSE(decrypt_result.ok());
154       EXPECT_EQ(absl::StatusCode::kInvalidArgument,
155                 decrypt_result.status().code());
156       EXPECT_PRED_FORMAT2(testing::IsSubstring, "decryption failed",
157                           std::string(decrypt_result.status().message()));
158     }
159 
160     {  // Correct ciphertext prefix.
161       std::string ciphertext =
162           prefix_id_1 + DummyHybridEncrypt(hybrid_name_1)
163                             .Encrypt(plaintext, context_info)
164                             .value();
165       auto decrypt_result = hybrid_decrypt->Decrypt(ciphertext, context_info);
166       EXPECT_TRUE(decrypt_result.ok()) << decrypt_result.status();
167       EXPECT_EQ(plaintext, decrypt_result.value());
168     }
169 
170     {  // Bad ciphertext.
171       std::string ciphertext = "some bad ciphertext";
172       auto decrypt_result = hybrid_decrypt->Decrypt(ciphertext, context_info);
173       EXPECT_FALSE(decrypt_result.ok());
174       EXPECT_EQ(absl::StatusCode::kInvalidArgument,
175           decrypt_result.status().code());
176       EXPECT_PRED_FORMAT2(testing::IsSubstring, "decryption failed",
177                           std::string(decrypt_result.status().message()));
178     }
179   }
180 }
181 
PopulateKeyInfo(uint32_t key_id,OutputPrefixType out_prefix_type,KeyStatusType status)182 KeysetInfo::KeyInfo PopulateKeyInfo(uint32_t key_id,
183                                     OutputPrefixType out_prefix_type,
184                                     KeyStatusType status) {
185   KeysetInfo::KeyInfo key_info;
186   key_info.set_output_prefix_type(out_prefix_type);
187   key_info.set_key_id(key_id);
188   key_info.set_status(status);
189   return key_info;
190 }
191 
192 // Creates a test keyset info object.
CreateTestKeysetInfo()193 KeysetInfo CreateTestKeysetInfo() {
194   KeysetInfo keyset_info;
195   *keyset_info.add_key_info() =
196       PopulateKeyInfo(/*key_id=*/1234543, OutputPrefixType::TINK,
197                       /*status=*/KeyStatusType::ENABLED);
198   *keyset_info.add_key_info() =
199       PopulateKeyInfo(/*key_id=*/726329, OutputPrefixType::LEGACY,
200                       /*status=*/KeyStatusType::ENABLED);
201   *keyset_info.add_key_info() =
202       PopulateKeyInfo(/*key_id=*/7213743, OutputPrefixType::TINK,
203                       /*status=*/KeyStatusType::ENABLED);
204   return keyset_info;
205 }
206 
207 // Tests for the monitoring behavior.
208 class HybridDecryptSetWrapperWithMonitoringTest : public Test {
209  protected:
210   // Perform some common initialization: reset the global registry, set expected
211   // calls for the mock monitoring factory and the returned clients.
SetUp()212   void SetUp() override {
213     Registry::Reset();
214 
215     // Setup mocks for catching Monitoring calls.
216     auto monitoring_client_factory =
217         absl::make_unique<MockMonitoringClientFactory>();
218     auto decryption_monitoring_client =
219         absl::make_unique<NiceMock<MockMonitoringClient>>();
220     decryption_monitoring_client_ = decryption_monitoring_client.get();
221 
222     // Monitoring tests expect that the client factory will create the
223     // corresponding MockMonitoringClients.
224     EXPECT_CALL(*monitoring_client_factory, New(_))
225         .WillOnce(
226             Return(ByMove(util::StatusOr<std::unique_ptr<MonitoringClient>>(
227                 std::move(decryption_monitoring_client)))));
228 
229     ASSERT_THAT(internal::RegistryImpl::GlobalInstance()
230                     .RegisterMonitoringClientFactory(
231                         std::move(monitoring_client_factory)),
232                 IsOk());
233     ASSERT_THAT(
234         internal::RegistryImpl::GlobalInstance().GetMonitoringClientFactory(),
235         Not(IsNull()));
236   }
237 
238   // Cleanup the registry to avoid mock leaks.
~HybridDecryptSetWrapperWithMonitoringTest()239   ~HybridDecryptSetWrapperWithMonitoringTest() override { Registry::Reset(); }
240 
241   MockMonitoringClient* decryption_monitoring_client_;
242 };
243 
244 // Test that successful encrypt operations are logged.
TEST_F(HybridDecryptSetWrapperWithMonitoringTest,WrapKeysetWithMonitoringEncryptSuccess)245 TEST_F(HybridDecryptSetWrapperWithMonitoringTest,
246        WrapKeysetWithMonitoringEncryptSuccess) {
247   // Create a primitive set and fill it with some entries
248   KeysetInfo keyset_info = CreateTestKeysetInfo();
249   const absl::flat_hash_map<std::string, std::string> annotations = {
250       {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}};
251   auto hybrid_decrypt_primitive_set =
252       absl::make_unique<PrimitiveSet<HybridDecrypt>>(annotations);
253   ASSERT_THAT(
254       hybrid_decrypt_primitive_set
255           ->AddPrimitive(absl::make_unique<DummyHybridDecrypt>("hybrid0"),
256                          keyset_info.key_info(0))
257           .status(),
258       IsOk());
259   ASSERT_THAT(
260       hybrid_decrypt_primitive_set
261           ->AddPrimitive(absl::make_unique<DummyHybridDecrypt>("hybrid1"),
262                          keyset_info.key_info(1))
263           .status(),
264       IsOk());
265   // Set the last as primary.
266   util::StatusOr<PrimitiveSet<HybridDecrypt>::Entry<HybridDecrypt>*> last =
267       hybrid_decrypt_primitive_set->AddPrimitive(
268           absl::make_unique<DummyHybridDecrypt>("hybrid2"),
269           keyset_info.key_info(2));
270   ASSERT_THAT(last, IsOk());
271   ASSERT_THAT(hybrid_decrypt_primitive_set->set_primary(*last), IsOk());
272   // Record the ID of the primary key.
273   const uint32_t primary_key_id = keyset_info.key_info(2).key_id();
274 
275   // Create a Hybrid Encrypt and encrypt some data, so we can decrypt it later.
276   util::StatusOr<std::unique_ptr<HybridDecrypt>> hybrid_decrypt =
277       HybridDecryptWrapper().Wrap(std::move(hybrid_decrypt_primitive_set));
278   ASSERT_THAT(hybrid_decrypt, IsOkAndHolds(NotNull()));
279 
280   constexpr absl::string_view plaintext = "This is some plaintext!";
281   constexpr absl::string_view context = "Some context!";
282   std::string ciphertext = absl::StrCat((*last)->get_identifier(),
283       DummyHybridEncrypt("hybrid2").Encrypt(plaintext, context).value());
284 
285   // Check that calling Decrypt triggers a Log() call.
286   EXPECT_CALL(*decryption_monitoring_client_,
287              Log(primary_key_id, ciphertext.size()));
288   EXPECT_THAT((*hybrid_decrypt)->Decrypt(ciphertext, context),
289              IsOkAndHolds(plaintext));
290 }
291 
TEST_F(HybridDecryptSetWrapperWithMonitoringTest,WrapKeysetWithMonitoringEncryptFailures)292 TEST_F(HybridDecryptSetWrapperWithMonitoringTest,
293        WrapKeysetWithMonitoringEncryptFailures) {
294   // Create a primitive set and fill it with some entries
295   KeysetInfo keyset_info = CreateTestKeysetInfo();
296   const absl::flat_hash_map<std::string, std::string> annotations = {
297       {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}};
298   auto hybrid_decrypt_primitive_set =
299       absl::make_unique<PrimitiveSet<HybridDecrypt>>(annotations);
300   ASSERT_THAT(hybrid_decrypt_primitive_set
301                   ->AddPrimitive(CreateAlwaysFailingHybridDecrypt("hybrid0"),
302                                  keyset_info.key_info(0))
303                   .status(),
304               IsOk());
305   ASSERT_THAT(hybrid_decrypt_primitive_set
306                   ->AddPrimitive(CreateAlwaysFailingHybridDecrypt("hybrid1"),
307                                  keyset_info.key_info(1))
308                   .status(),
309               IsOk());
310   // Set the last as primary.
311   util::StatusOr<PrimitiveSet<HybridDecrypt>::Entry<HybridDecrypt>*> last =
312       hybrid_decrypt_primitive_set->AddPrimitive(
313           CreateAlwaysFailingHybridDecrypt("hybrid2"), keyset_info.key_info(2));
314   ASSERT_THAT(last, IsOkAndHolds(NotNull()));
315   ASSERT_THAT(hybrid_decrypt_primitive_set->set_primary(*last), IsOk());
316 
317   // Create a Hybrid Decrypt and decrypt some invalid ciphertext.
318   util::StatusOr<std::unique_ptr<HybridDecrypt>> hybrid_decrypt =
319       HybridDecryptWrapper().Wrap(std::move(hybrid_decrypt_primitive_set));
320   ASSERT_THAT(hybrid_decrypt, IsOk());
321 
322   constexpr absl::string_view ciphertext = "This is some ciphertext!";
323   constexpr absl::string_view context = "Some context!";
324 
325   // Check that calling Decrypt triggers a LogFailure() call.
326   EXPECT_CALL(*decryption_monitoring_client_, LogFailure());
327   EXPECT_THAT((*hybrid_decrypt)->Decrypt(ciphertext, context).status(),
328               StatusIs(absl::StatusCode::kInvalidArgument));
329 }
330 
331 }  // namespace
332 }  // namespace tink
333 }  // namespace crypto
334