xref: /aosp_15_r20/external/tink/cc/streamingaead/streaming_aead_wrapper_test.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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