1 // Copyright 2019 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/streamingaead/streaming_aead_wrapper.h"
18
19 #include <memory>
20 #include <sstream>
21 #include <string>
22 #include <utility>
23 #include <vector>
24
25 #include "gtest/gtest.h"
26 #include "absl/memory/memory.h"
27 #include "absl/status/status.h"
28 #include "absl/strings/str_cat.h"
29 #include "absl/strings/string_view.h"
30 #include "tink/input_stream.h"
31 #include "tink/insecure_secret_key_access.h"
32 #include "tink/internal/test_random_access_stream.h"
33 #include "tink/output_stream.h"
34 #include "tink/primitive_set.h"
35 #include "tink/proto_keyset_format.h"
36 #include "tink/random_access_stream.h"
37 #include "tink/streaming_aead.h"
38 #include "tink/streamingaead/aes_gcm_hkdf_streaming_key_manager.h"
39 #include "tink/streamingaead/streaming_aead_config.h"
40 #include "tink/subtle/random.h"
41 #include "tink/subtle/streaming_aead_test_util.h"
42 #include "tink/subtle/test_util.h"
43 #include "tink/util/buffer.h"
44 #include "tink/util/istream_input_stream.h"
45 #include "tink/util/ostream_output_stream.h"
46 #include "tink/util/status.h"
47 #include "tink/util/test_matchers.h"
48 #include "tink/util/test_util.h"
49 #include "proto/aes_gcm_hkdf_streaming.pb.h"
50 #include "proto/common.pb.h"
51 #include "proto/tink.pb.h"
52
53 namespace crypto {
54 namespace tink {
55 namespace {
56
57 using ::crypto::tink::test::DummyStreamingAead;
58 using ::crypto::tink::test::IsOk;
59 using ::crypto::tink::test::StatusIs;
60 using ::google::crypto::tink::KeysetInfo;
61 using ::google::crypto::tink::KeyStatusType;
62 using ::google::crypto::tink::OutputPrefixType;
63 using ::crypto::tink::subtle::test::ReadFromStream;
64 using ::crypto::tink::subtle::test::WriteToStream;
65 using ::testing::HasSubstr;
66
67 // A container for specification of instances of DummyStreamingAead
68 // to be created for testing.
69 struct StreamingAeadSpec {
70 uint32_t key_id;
71 std::string saead_name;
72 OutputPrefixType output_prefix_type;
73 };
74
75 // Generates a PrimitiveSet<StreamingAead> with DummyStreamingAead
76 // instances according to the specification in 'spec'.
77 // The last entry in 'spec' will be the primary primitive in the returned set.
GetTestStreamingAeadSet(const std::vector<StreamingAeadSpec> & spec)78 std::unique_ptr<PrimitiveSet<StreamingAead>> GetTestStreamingAeadSet(
79 const std::vector<StreamingAeadSpec>& spec) {
80 auto saead_set = absl::make_unique<PrimitiveSet<StreamingAead>>();
81 int i = 0;
82 for (auto& s : spec) {
83 KeysetInfo::KeyInfo key_info;
84 key_info.set_output_prefix_type(s.output_prefix_type);
85 key_info.set_key_id(s.key_id);
86 key_info.set_status(KeyStatusType::ENABLED);
87 std::unique_ptr<StreamingAead> saead =
88 absl::make_unique<DummyStreamingAead>(s.saead_name);
89 auto entry_result = saead_set->AddPrimitive(std::move(saead), key_info);
90 EXPECT_TRUE(entry_result.ok());
91 if (i + 1 == spec.size()) {
92 EXPECT_THAT(saead_set->set_primary(entry_result.value()), IsOk());
93 }
94 i++;
95 }
96 return saead_set;
97 }
98
TEST(StreamingAeadSetWrapperTest,WrapNullptr)99 TEST(StreamingAeadSetWrapperTest, WrapNullptr) {
100 StreamingAeadWrapper wrapper;
101 auto result = wrapper.Wrap(nullptr);
102 EXPECT_FALSE(result.ok());
103 EXPECT_EQ(absl::StatusCode::kInternal, result.status().code());
104 EXPECT_PRED_FORMAT2(testing::IsSubstring, "non-NULL",
105 std::string(result.status().message()));
106 }
107
TEST(StreamingAeadSetWrapperTest,WrapEmpty)108 TEST(StreamingAeadSetWrapperTest, WrapEmpty) {
109 StreamingAeadWrapper wrapper;
110 auto result = wrapper.Wrap(absl::make_unique<PrimitiveSet<StreamingAead>>());
111 EXPECT_FALSE(result.ok());
112 EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
113 EXPECT_PRED_FORMAT2(testing::IsSubstring, "no primary",
114 std::string(result.status().message()));
115 }
116
TEST(StreamingAeadSetWrapperTest,BasicEncryptionAndDecryption)117 TEST(StreamingAeadSetWrapperTest, BasicEncryptionAndDecryption) {
118 uint32_t key_id_0 = 1234543;
119 uint32_t key_id_1 = 726329;
120 uint32_t key_id_2 = 7213743;
121 std::string saead_name_0 = "streaming_aead0";
122 std::string saead_name_1 = "streaming_aead1";
123 std::string saead_name_2 = "streaming_aead2";
124
125 auto saead_set = GetTestStreamingAeadSet(
126 {{key_id_0, saead_name_0, OutputPrefixType::RAW},
127 {key_id_1, saead_name_1, OutputPrefixType::RAW},
128 {key_id_2, saead_name_2, OutputPrefixType::RAW}});
129
130 // Wrap saead_set and test the resulting StreamingAead.
131 StreamingAeadWrapper wrapper;
132 auto wrap_result = wrapper.Wrap(std::move(saead_set));
133 EXPECT_TRUE(wrap_result.ok()) << wrap_result.status();
134 auto saead = std::move(wrap_result.value());
135 for (int pt_size : {0, 1, 10, 100, 10000}) {
136 std::string plaintext = subtle::Random::GetRandomBytes(pt_size);
137 for (std::string aad : {"some_aad", "", "some other aad"}) {
138 SCOPED_TRACE(absl::StrCat("pt_size = ", pt_size,
139 ", aad = '", aad, "'"));
140
141 // Prepare ciphertext destination stream.
142 auto ct_stream = absl::make_unique<std::stringstream>();
143 // A reference to the ciphertext buffer, for later validation.
144 auto ct_buf = ct_stream->rdbuf();
145 std::unique_ptr<OutputStream> ct_destination(
146 absl::make_unique<util::OstreamOutputStream>(std::move(ct_stream)));
147 // Encrypt the plaintext.
148 auto enc_stream_result =
149 saead->NewEncryptingStream(std::move(ct_destination), aad);
150 EXPECT_THAT(enc_stream_result, IsOk());
151 auto enc_stream = std::move(enc_stream_result.value());
152 auto status = WriteToStream(enc_stream.get(), plaintext);
153 EXPECT_THAT(status, IsOk());
154 EXPECT_EQ(absl::StrCat(saead_name_2, aad, plaintext), ct_buf->str());
155 // Prepare ciphertext source stream.
156 auto ct_source_stream =
157 absl::make_unique<std::stringstream>(ct_buf->str());
158 std::unique_ptr<InputStream> ct_source(
159 absl::make_unique<util::IstreamInputStream>(
160 std::move(ct_source_stream)));
161 // Decrypt the ciphertext.
162 auto dec_stream_result =
163 saead->NewDecryptingStream(std::move(ct_source), aad);
164 EXPECT_THAT(dec_stream_result, IsOk());
165 std::string decrypted;
166 status = ReadFromStream(dec_stream_result.value().get(), &decrypted);
167 EXPECT_THAT(status, IsOk());
168 EXPECT_EQ(plaintext, decrypted);
169 }
170 }
171 }
172
TEST(StreamingAeadSetWrapperTest,DecryptionWithRandomAccessStream)173 TEST(StreamingAeadSetWrapperTest, DecryptionWithRandomAccessStream) {
174 uint32_t key_id_0 = 1234543;
175 uint32_t key_id_1 = 726329;
176 uint32_t key_id_2 = 7213743;
177 std::string saead_name_0 = "streaming_aead0";
178 std::string saead_name_1 = "streaming_aead1";
179 std::string saead_name_2 = "streaming_aead2";
180
181 auto saead_set = GetTestStreamingAeadSet(
182 {{key_id_0, saead_name_0, OutputPrefixType::RAW},
183 {key_id_1, saead_name_1, OutputPrefixType::RAW},
184 {key_id_2, saead_name_2, OutputPrefixType::RAW}});
185
186 // Wrap saead_set and test the resulting StreamingAead.
187 StreamingAeadWrapper wrapper;
188 auto wrap_result = wrapper.Wrap(std::move(saead_set));
189 EXPECT_TRUE(wrap_result.ok()) << wrap_result.status();
190 auto saead = std::move(wrap_result.value());
191 for (int pt_size : {0, 1, 10, 100, 10000}) {
192 std::string plaintext = subtle::Random::GetRandomBytes(pt_size);
193 for (std::string aad : {"some_aad", "", "some other aad"}) {
194 SCOPED_TRACE(absl::StrCat("pt_size = ", pt_size,
195 ", aad = '", aad, "'"));
196
197 // Prepare ciphertext destination stream.
198 auto ct_stream = absl::make_unique<std::stringstream>();
199 // A reference to the ciphertext buffer, for later validation.
200 auto ct_buf = ct_stream->rdbuf();
201 std::unique_ptr<OutputStream> ct_destination(
202 absl::make_unique<util::OstreamOutputStream>(std::move(ct_stream)));
203
204 // Encrypt the plaintext.
205 auto enc_stream_result =
206 saead->NewEncryptingStream(std::move(ct_destination), aad);
207 EXPECT_THAT(enc_stream_result, IsOk());
208 auto enc_stream = std::move(enc_stream_result.value());
209 auto status = WriteToStream(enc_stream.get(), plaintext);
210 EXPECT_THAT(status, IsOk());
211 EXPECT_EQ(absl::StrCat(saead_name_2, aad, plaintext), ct_buf->str());
212
213 // Decrypt the ciphertext.
214 auto ct_source =
215 std::make_unique<internal::TestRandomAccessStream>(ct_buf->str());
216 auto dec_stream_result =
217 saead->NewDecryptingRandomAccessStream(std::move(ct_source), aad);
218 EXPECT_THAT(dec_stream_result, IsOk());
219 std::string decrypted;
220 status = internal::ReadAllFromRandomAccessStream(
221 dec_stream_result.value().get(), decrypted);
222 EXPECT_THAT(status, StatusIs(absl::StatusCode::kOutOfRange,
223 HasSubstr("EOF")));
224 EXPECT_EQ(plaintext, decrypted);
225 }
226 }
227 }
228
TEST(StreamingAeadSetWrapperTest,DecryptionAfterWrapperIsDestroyed)229 TEST(StreamingAeadSetWrapperTest, DecryptionAfterWrapperIsDestroyed) {
230 uint32_t key_id_0 = 1234543;
231 uint32_t key_id_1 = 726329;
232 uint32_t key_id_2 = 7213743;
233 std::string saead_name_0 = "streaming_aead0";
234 std::string saead_name_1 = "streaming_aead1";
235 std::string saead_name_2 = "streaming_aead2";
236
237 auto saead_set = GetTestStreamingAeadSet(
238 {{key_id_0, saead_name_0, OutputPrefixType::RAW},
239 {key_id_1, saead_name_1, OutputPrefixType::RAW},
240 {key_id_2, saead_name_2, OutputPrefixType::RAW}});
241
242 int pt_size = 100;
243 std::string plaintext = subtle::Random::GetRandomBytes(pt_size);
244 std::string aad = "some_aad";
245 std::unique_ptr<InputStream> dec_stream;
246 {
247 // Wrap saead_set and test the resulting StreamingAead.
248 StreamingAeadWrapper wrapper;
249 auto wrap_result = wrapper.Wrap(std::move(saead_set));
250 EXPECT_TRUE(wrap_result.ok()) << wrap_result.status();
251 auto saead = std::move(wrap_result.value());
252
253 // Prepare ciphertext destination stream.
254 auto ct_stream = absl::make_unique<std::stringstream>();
255 // A reference to the ciphertext buffer, for later validation.
256 auto ct_buf = ct_stream->rdbuf();
257 std::unique_ptr<OutputStream> ct_destination(
258 absl::make_unique<util::OstreamOutputStream>(std::move(ct_stream)));
259 // Encrypt the plaintext.
260 auto enc_stream_result =
261 saead->NewEncryptingStream(std::move(ct_destination), aad);
262 EXPECT_THAT(enc_stream_result, IsOk());
263 auto enc_stream = std::move(enc_stream_result.value());
264 auto status = WriteToStream(enc_stream.get(), plaintext);
265 EXPECT_THAT(status, IsOk());
266 EXPECT_EQ(absl::StrCat(saead_name_2, aad, plaintext), ct_buf->str());
267 // Prepare ciphertext source stream.
268 auto ct_source_stream =
269 absl::make_unique<std::stringstream>(ct_buf->str());
270 std::unique_ptr<InputStream> ct_source(
271 absl::make_unique<util::IstreamInputStream>(
272 std::move(ct_source_stream)));
273 // Decrypt the ciphertext.
274 auto dec_stream_result =
275 saead->NewDecryptingStream(std::move(ct_source), aad);
276 EXPECT_THAT(dec_stream_result, IsOk());
277 dec_stream = std::move(dec_stream_result.value());
278 }
279 // Now wrapper and saead are out of scope,
280 // but decrypting stream should still work.
281 std::string decrypted;
282 auto status = ReadFromStream(dec_stream.get(), &decrypted);
283 EXPECT_THAT(status, IsOk());
284 EXPECT_EQ(plaintext, decrypted);
285 }
286
TEST(StreamingAeadSetWrapperTest,EncryptWithTink)287 TEST(StreamingAeadSetWrapperTest, EncryptWithTink) {
288 ASSERT_THAT(StreamingAeadConfig::Register(), IsOk());
289
290 google::crypto::tink::AesGcmHkdfStreamingKey key;
291 key.set_key_value("0123456789012345");
292 google::crypto::tink::AesGcmHkdfStreamingParams& params =
293 *key.mutable_params();
294 params.set_hkdf_hash_type(google::crypto::tink::HashType::SHA1);
295 params.set_derived_key_size(16);
296 params.set_ciphertext_segment_size(1024);
297
298 std::string serialized_key_1 = key.SerializeAsString();
299
300 key.set_key_value("0123456789abcdef");
301 std::string serialized_key_2 = key.SerializeAsString();
302
303 google::crypto::tink::Keyset keyset;
304 {
305 google::crypto::tink::Keyset::Key& keyset_key = *keyset.add_key();
306 google::crypto::tink::KeyData& key_data = *keyset_key.mutable_key_data();
307 key_data.set_type_url(AesGcmHkdfStreamingKeyManager().get_key_type());
308 key_data.set_value(serialized_key_1);
309 key_data.set_key_material_type(google::crypto::tink::KeyData::SYMMETRIC);
310 keyset_key.set_key_id(1);
311 keyset_key.set_output_prefix_type(
312 google::crypto::tink::OutputPrefixType::TINK);
313 keyset_key.set_status(google::crypto::tink::KeyStatusType::ENABLED);
314
315 keyset.set_primary_key_id(1);
316 }
317 {
318 google::crypto::tink::Keyset::Key& keyset_key = *keyset.add_key();
319 google::crypto::tink::KeyData& key_data = *keyset_key.mutable_key_data();
320 key_data.set_type_url(AesGcmHkdfStreamingKeyManager().get_key_type());
321 key_data.set_value(serialized_key_2);
322 key_data.set_key_material_type(google::crypto::tink::KeyData::SYMMETRIC);
323 keyset_key.set_key_id(2);
324 keyset_key.set_output_prefix_type(
325 google::crypto::tink::OutputPrefixType::RAW);
326 keyset_key.set_status(google::crypto::tink::KeyStatusType::ENABLED);
327 }
328
329 crypto::tink::util::StatusOr<KeysetHandle> handle =
330 ParseKeysetFromProtoKeysetFormat(keyset.SerializeAsString(),
331 InsecureSecretKeyAccess::Get());
332 ASSERT_THAT(handle.status(), IsOk());
333
334 crypto::tink::util::StatusOr<std::unique_ptr<StreamingAead>> streaming_aead =
335 handle->GetPrimitive<StreamingAead>();
336
337 ASSERT_THAT(streaming_aead.status(), IsOk());
338
339 EXPECT_THAT(EncryptThenDecrypt(streaming_aead.value().get(),
340 streaming_aead.value().get(),
341 subtle::Random::GetRandomBytes(10000),
342 "some associated data", 0),
343 IsOk());
344 }
345
346 } // namespace
347 } // namespace tink
348 } // namespace crypto
349