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