1 // Copyright 2022 Google LLC
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/internal/hpke_context.h"
18
19 #include <memory>
20 #include <string>
21 #include <vector>
22
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "tink/hybrid/internal/hpke_test_util.h"
26 #include "tink/hybrid/internal/hpke_util.h"
27 #include "tink/util/secret_data.h"
28 #include "tink/util/statusor.h"
29 #include "tink/util/test_matchers.h"
30
31 namespace crypto {
32 namespace tink {
33 namespace internal {
34 namespace {
35
36 using ::crypto::tink::internal::CreateHpkeTestParams;
37 using ::crypto::tink::internal::DefaultHpkeTestParams;
38 using ::crypto::tink::internal::HpkeTestParams;
39 using ::crypto::tink::test::IsOk;
40 using ::crypto::tink::test::StatusIs;
41 using ::testing::Eq;
42 using ::testing::TestWithParam;
43 using ::testing::Values;
44
45 class HpkeContextTest : public TestWithParam<HpkeParams> {};
46
47 INSTANTIATE_TEST_SUITE_P(
48 HpkeContextTestSuite, HpkeContextTest,
49 Values(HpkeParams{HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
50 HpkeAead::kAes128Gcm},
51 HpkeParams{HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
52 HpkeAead::kChaCha20Poly1305}));
53
TEST_P(HpkeContextTest,SealAndOpen)54 TEST_P(HpkeContextTest, SealAndOpen) {
55 HpkeParams hpke_params = GetParam();
56 util::StatusOr<HpkeTestParams> params = CreateHpkeTestParams(hpke_params);
57 ASSERT_THAT(params, IsOk());
58
59 util::StatusOr<std::unique_ptr<HpkeContext>> sender_hpke_context =
60 HpkeContext::SetupSender(hpke_params, params->recipient_public_key,
61 params->application_info);
62 ASSERT_THAT(sender_hpke_context, IsOk());
63
64 util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
65 HpkeContext::SetupRecipient(
66 hpke_params,
67 util::SecretDataFromStringView(params->recipient_private_key),
68 (*sender_hpke_context)->EncapsulatedKey(), params->application_info);
69 ASSERT_THAT(recipient_hpke_context, IsOk());
70
71 std::vector<std::string> inputs = {"", params->plaintext};
72 std::vector<std::string> context_infos = {"", params->application_info};
73 for (const std::string& input : inputs) {
74 for (const std::string& context_info : context_infos) {
75 SCOPED_TRACE(absl::StrCat("plaintext: '", input, "', context_info: '",
76 context_info, "'"));
77 util::StatusOr<std::string> ciphertext =
78 (*sender_hpke_context)->Seal(input, context_info);
79 ASSERT_THAT(ciphertext, IsOk());
80
81 util::StatusOr<std::string> plaintext =
82 (*recipient_hpke_context)->Open(*ciphertext, context_info);
83 ASSERT_THAT(plaintext, IsOk());
84
85 EXPECT_THAT(*plaintext, Eq(input));
86 }
87 }
88 }
89
TEST_P(HpkeContextTest,Export)90 TEST_P(HpkeContextTest, Export) {
91 HpkeParams hpke_params = GetParam();
92 util::StatusOr<HpkeTestParams> params = CreateHpkeTestParams(hpke_params);
93 ASSERT_THAT(params, IsOk());
94
95 util::StatusOr<std::unique_ptr<HpkeContext>> sender_hpke_context =
96 HpkeContext::SetupSender(hpke_params, params->recipient_public_key,
97 params->application_info);
98 ASSERT_THAT(sender_hpke_context, IsOk());
99
100 util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
101 HpkeContext::SetupRecipient(
102 hpke_params,
103 util::SecretDataFromStringView(params->recipient_private_key),
104 (*sender_hpke_context)->EncapsulatedKey(), params->application_info);
105 ASSERT_THAT(recipient_hpke_context, IsOk());
106
107 std::vector<std::string> exporter_contexts = {"", "c", "context"};
108 std::vector<int> secret_lengths = {0, 8, 16, 32, 64};
109 for (const std::string& exporter_context : exporter_contexts) {
110 for (int secret_length : secret_lengths) {
111 SCOPED_TRACE(absl::StrCat("exporter_context: '", exporter_context,
112 "', secret_length: '", secret_length, "'"));
113 util::StatusOr<util::SecretData> sender_secret =
114 (*sender_hpke_context)->Export(exporter_context, secret_length);
115 ASSERT_THAT(sender_secret, IsOk());
116
117 util::StatusOr<util::SecretData> recipient_secret =
118 (*recipient_hpke_context)->Export(exporter_context, secret_length);
119 ASSERT_THAT(recipient_secret, IsOk());
120
121 EXPECT_THAT(*sender_secret, Eq(*recipient_secret));
122 }
123 }
124 }
125
TEST_P(HpkeContextTest,OpenTruncatedCiphertextFails)126 TEST_P(HpkeContextTest, OpenTruncatedCiphertextFails) {
127 HpkeParams hpke_params = GetParam();
128 util::StatusOr<HpkeTestParams> params = CreateHpkeTestParams(hpke_params);
129 ASSERT_THAT(params, IsOk());
130
131 util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
132 HpkeContext::SetupRecipient(
133 hpke_params,
134 util::SecretDataFromStringView(params->recipient_private_key),
135 params->encapsulated_key, params->application_info);
136 ASSERT_THAT(recipient_hpke_context, IsOk());
137
138 util::StatusOr<std::string> plaintext =
139 (*recipient_hpke_context)
140 ->Open(params->ciphertext, params->associated_data);
141 ASSERT_THAT(plaintext, IsOk());
142
143 const std::string truncated_ciphertext =
144 params->ciphertext.substr(params->ciphertext.length() - 1);
145 util::StatusOr<std::string> bad_plaintext =
146 (*recipient_hpke_context)
147 ->Open(truncated_ciphertext, params->associated_data);
148 EXPECT_THAT(bad_plaintext.status(), StatusIs(absl::StatusCode::kUnknown));
149 }
150
TEST_P(HpkeContextTest,OpenModifiedCiphertextFails)151 TEST_P(HpkeContextTest, OpenModifiedCiphertextFails) {
152 HpkeParams hpke_params = GetParam();
153 util::StatusOr<HpkeTestParams> params = CreateHpkeTestParams(hpke_params);
154 ASSERT_THAT(params, IsOk());
155
156 util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
157 HpkeContext::SetupRecipient(
158 hpke_params,
159 util::SecretDataFromStringView(params->recipient_private_key),
160 params->encapsulated_key, params->application_info);
161 ASSERT_THAT(recipient_hpke_context, IsOk());
162
163 util::StatusOr<std::string> plaintext =
164 (*recipient_hpke_context)
165 ->Open(params->ciphertext, params->associated_data);
166 ASSERT_THAT(plaintext, IsOk());
167
168 const std::string modified_ciphertext =
169 absl::StrCat(params->ciphertext, "modification");
170 util::StatusOr<std::string> bad_plaintext =
171 (*recipient_hpke_context)
172 ->Open(modified_ciphertext, params->associated_data);
173 EXPECT_THAT(bad_plaintext.status(), StatusIs(absl::StatusCode::kUnknown));
174 }
175
TEST_P(HpkeContextTest,OpenModifiedAssociatedDataFails)176 TEST_P(HpkeContextTest, OpenModifiedAssociatedDataFails) {
177 HpkeParams hpke_params = GetParam();
178 util::StatusOr<HpkeTestParams> params = CreateHpkeTestParams(hpke_params);
179 ASSERT_THAT(params, IsOk());
180
181 util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
182 HpkeContext::SetupRecipient(
183 hpke_params,
184 util::SecretDataFromStringView(params->recipient_private_key),
185 params->encapsulated_key, params->application_info);
186 ASSERT_THAT(recipient_hpke_context, IsOk());
187
188 util::StatusOr<std::string> plaintext =
189 (*recipient_hpke_context)
190 ->Open(params->ciphertext, params->associated_data);
191 ASSERT_THAT(plaintext, IsOk());
192
193 const std::string modified_associated_data =
194 absl::StrCat(params->associated_data, "modification");
195 util::StatusOr<std::string> bad_plaintext =
196 (*recipient_hpke_context)
197 ->Open(params->ciphertext, modified_associated_data);
198 EXPECT_THAT(bad_plaintext.status(), StatusIs(absl::StatusCode::kUnknown));
199 }
200
201 class HpkeContextWithBadHpkeParamTest : public TestWithParam<HpkeParams> {};
202
203 INSTANTIATE_TEST_SUITE_P(
204 HpkeContextWithBadHpkeParamTestTestSuite, HpkeContextWithBadHpkeParamTest,
205 Values(HpkeParams{HpkeKem::kUnknownKem, HpkeKdf::kHkdfSha256,
206 HpkeAead::kAes128Gcm},
207 HpkeParams{HpkeKem::kX25519HkdfSha256, HpkeKdf::kUnknownKdf,
208 HpkeAead::kAes256Gcm},
209 HpkeParams{HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
210 HpkeAead::kUnknownAead}));
211
TEST_P(HpkeContextWithBadHpkeParamTest,SenderBadHpkeParamFails)212 TEST_P(HpkeContextWithBadHpkeParamTest, SenderBadHpkeParamFails) {
213 HpkeParams hpke_params = GetParam();
214 HpkeTestParams params = DefaultHpkeTestParams();
215
216 util::StatusOr<std::unique_ptr<HpkeContext>> sender_hpke_context =
217 HpkeContext::SetupSender(hpke_params, params.recipient_public_key,
218 params.application_info);
219 EXPECT_THAT(sender_hpke_context.status(),
220 StatusIs(absl::StatusCode::kInvalidArgument));
221 }
222
TEST_P(HpkeContextWithBadHpkeParamTest,RecipientBadHpkeParamFails)223 TEST_P(HpkeContextWithBadHpkeParamTest, RecipientBadHpkeParamFails) {
224 HpkeParams hpke_params = GetParam();
225 HpkeTestParams params = DefaultHpkeTestParams();
226
227 util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
228 HpkeContext::SetupRecipient(
229 hpke_params,
230 util::SecretDataFromStringView(params.recipient_private_key),
231 params.encapsulated_key, params.application_info);
232 EXPECT_THAT(recipient_hpke_context.status(),
233 StatusIs(absl::StatusCode::kInvalidArgument));
234 }
235
TEST(HpkeContextZeroLengthKeyTest,ZeroLengthPublicKeyFails)236 TEST(HpkeContextZeroLengthKeyTest, ZeroLengthPublicKeyFails) {
237 HpkeParams hpke_params = {HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
238 HpkeAead::kAes256Gcm};
239 HpkeTestParams params = DefaultHpkeTestParams();
240
241 util::StatusOr<std::unique_ptr<HpkeContext>> sender_hpke_context =
242 HpkeContext::SetupSender(hpke_params, /*recipient_public_key=*/"",
243 params.application_info);
244 EXPECT_THAT(sender_hpke_context.status(),
245 StatusIs(absl::StatusCode::kInvalidArgument));
246 }
247
TEST(HpkeContextZeroLengthKeyTest,ZeroLengthPrivateKeyFails)248 TEST(HpkeContextZeroLengthKeyTest, ZeroLengthPrivateKeyFails) {
249 HpkeParams hpke_params = {HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
250 HpkeAead::kAes256Gcm};
251 HpkeTestParams params = DefaultHpkeTestParams();
252
253 util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
254 HpkeContext::SetupRecipient(
255 hpke_params,
256 /*recipient_private_key=*/util::SecretDataFromStringView(""),
257 params.encapsulated_key, params.application_info);
258 EXPECT_THAT(recipient_hpke_context.status(),
259 StatusIs(absl::StatusCode::kInvalidArgument));
260 }
261
TEST(HpkeContextZeroLengthKeyTest,ZeroLengthEncapsulatedKeyFails)262 TEST(HpkeContextZeroLengthKeyTest, ZeroLengthEncapsulatedKeyFails) {
263 HpkeParams hpke_params = {HpkeKem::kX25519HkdfSha256, HpkeKdf::kHkdfSha256,
264 HpkeAead::kAes256Gcm};
265 HpkeTestParams params = DefaultHpkeTestParams();
266
267 util::StatusOr<std::unique_ptr<HpkeContext>> recipient_hpke_context =
268 HpkeContext::SetupRecipient(
269 hpke_params,
270 util::SecretDataFromStringView(params.recipient_private_key),
271 /*encapsulated_key=*/"", params.application_info);
272 EXPECT_THAT(recipient_hpke_context.status(),
273 StatusIs(absl::StatusCode::kInvalidArgument));
274 }
275
TEST(ConcatenatePayloadTest,ConcatenatePayloadSucceeds)276 TEST(ConcatenatePayloadTest, ConcatenatePayloadSucceeds) {
277 HpkeTestParams params = DefaultHpkeTestParams();
278 EXPECT_THAT(ConcatenatePayload(params.encapsulated_key, params.ciphertext),
279 Eq(absl::StrCat(params.encapsulated_key, params.ciphertext)));
280 }
281
TEST(SplitPayloadTest,SplitPayloadSucceeds)282 TEST(SplitPayloadTest, SplitPayloadSucceeds) {
283 HpkeTestParams params = DefaultHpkeTestParams();
284 const std::string payload =
285 absl::StrCat(params.encapsulated_key, params.ciphertext);
286 util::StatusOr<HpkePayloadView> hpke_payload =
287 SplitPayload(HpkeKem::kX25519HkdfSha256, payload);
288 ASSERT_THAT(hpke_payload, IsOk());
289 EXPECT_THAT(hpke_payload->encapsulated_key, Eq(params.encapsulated_key));
290 EXPECT_THAT(hpke_payload->ciphertext, Eq(params.ciphertext));
291 }
292
TEST(SplitPayloadTest,InvalidKemFails)293 TEST(SplitPayloadTest, InvalidKemFails) {
294 HpkeTestParams params = DefaultHpkeTestParams();
295 util::StatusOr<HpkePayloadView> payload =
296 SplitPayload(HpkeKem::kUnknownKem,
297 absl::StrCat(params.encapsulated_key, params.ciphertext));
298 EXPECT_THAT(payload.status(), StatusIs(absl::StatusCode::kInvalidArgument));
299 }
300
301 } // namespace
302 } // namespace internal
303 } // namespace tink
304 } // namespace crypto
305