xref: /aosp_15_r20/external/tink/cc/streamingaead/decrypting_input_stream_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/decrypting_input_stream.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/output_stream.h"
32 #include "tink/primitive_set.h"
33 #include "tink/streaming_aead.h"
34 #include "tink/subtle/random.h"
35 #include "tink/subtle/test_util.h"
36 #include "tink/util/istream_input_stream.h"
37 #include "tink/util/ostream_output_stream.h"
38 #include "tink/util/status.h"
39 #include "tink/util/test_matchers.h"
40 #include "tink/util/test_util.h"
41 #include "proto/tink.pb.h"
42 
43 namespace crypto {
44 namespace tink {
45 namespace streamingaead {
46 namespace {
47 
48 using crypto::tink::test::DummyStreamingAead;
49 using crypto::tink::test::IsOk;
50 using crypto::tink::test::StatusIs;
51 using crypto::tink::util::IstreamInputStream;
52 using crypto::tink::util::OstreamOutputStream;
53 using google::crypto::tink::KeysetInfo;
54 using google::crypto::tink::KeyStatusType;
55 using google::crypto::tink::OutputPrefixType;
56 using subtle::test::ReadFromStream;
57 using subtle::test::WriteToStream;
58 
59 static int kBufferSize = 128;
60 
61 // Creates an InputStream with the specified contents.
GetInputStream(absl::string_view contents)62 std::unique_ptr<InputStream> GetInputStream(absl::string_view contents) {
63   // Prepare ciphertext source stream.
64   auto string_stream =
65       absl::make_unique<std::stringstream>(std::string(contents));
66   std::unique_ptr<InputStream> input_stream(
67       absl::make_unique<util::IstreamInputStream>(
68           std::move(string_stream), kBufferSize));
69   return input_stream;
70 }
71 
72 // Creates an InputStream that contains ciphertext resulting
73 // from encryption of 'pt' with 'aad' as associated data, using 'saead'.
GetCiphertextSource(StreamingAead * saead,absl::string_view pt,absl::string_view aad)74 std::unique_ptr<InputStream> GetCiphertextSource(StreamingAead* saead,
75                                                  absl::string_view pt,
76                                                  absl::string_view aad) {
77   // Prepare ciphertext destination stream.
78   auto ct_stream = absl::make_unique<std::stringstream>();
79   // A reference to the ciphertext buffer.
80   auto ct_buf = ct_stream->rdbuf();
81   std::unique_ptr<OutputStream> ct_destination(
82       absl::make_unique<OstreamOutputStream>(std::move(ct_stream)));
83 
84   // Compute the ciphertext.
85   auto enc_stream_result =
86       saead->NewEncryptingStream(std::move(ct_destination), aad);
87   EXPECT_THAT(enc_stream_result, IsOk());
88   EXPECT_THAT(WriteToStream(enc_stream_result.value().get(), pt), IsOk());
89 
90   // Return the ciphertext as InputStream.
91   auto ct_stream3 = absl::make_unique<std::stringstream>(ct_buf->str());
92   auto input =  absl::make_unique<IstreamInputStream>(std::move(ct_stream3));
93   std::string reads;
94   EXPECT_THAT(ReadFromStream(input.get(), &reads), IsOk());
95   auto ct_stream2 = absl::make_unique<std::stringstream>(ct_buf->str());
96   return absl::make_unique<IstreamInputStream>(std::move(ct_stream2));
97 }
98 
99 // A container for specification of instances of DummyStreamingAead
100 // to be created for testing.
101 struct StreamingAeadSpec {
102   uint32_t key_id;
103   std::string saead_name;
104 };
105 
106 // Generates a PrimitiveSet<StreamingAead> with DummyStreamingAead
107 // instances according to the specification in 'spec'.
108 // The last entry in 'spec' will be the primary primitive in the returned set.
GetTestStreamingAeadSet(const std::vector<StreamingAeadSpec> & spec)109 std::shared_ptr<PrimitiveSet<StreamingAead>> GetTestStreamingAeadSet(
110     const std::vector<StreamingAeadSpec>& spec) {
111   std::shared_ptr<PrimitiveSet<StreamingAead>> saead_set =
112       std::make_shared<PrimitiveSet<StreamingAead>>();
113   int i = 0;
114   for (auto& s : spec) {
115     KeysetInfo::KeyInfo key_info;
116     key_info.set_output_prefix_type(OutputPrefixType::RAW);
117     key_info.set_key_id(s.key_id);
118     key_info.set_status(KeyStatusType::ENABLED);
119     std::unique_ptr<StreamingAead> saead =
120         absl::make_unique<DummyStreamingAead>(s.saead_name);
121     auto entry_result = saead_set->AddPrimitive(std::move(saead), key_info);
122     EXPECT_TRUE(entry_result.ok());
123     if (i + 1 == spec.size()) {
124       EXPECT_THAT(saead_set->set_primary(entry_result.value()), IsOk());
125     }
126     i++;
127   }
128   return saead_set;
129 }
130 
TEST(DecryptingInputStreamTest,BasicDecryption)131 TEST(DecryptingInputStreamTest, BasicDecryption) {
132   uint32_t key_id_0 = 1234543;
133   uint32_t key_id_1 = 726329;
134   uint32_t key_id_2 = 7213743;
135   std::string saead_name_0 = "streaming_aead0";
136   std::string saead_name_1 = "streaming_aead1";
137   std::string saead_name_2 = "streaming_aead2";
138 
139   auto saead_set = GetTestStreamingAeadSet(
140       {{key_id_0, saead_name_0}, {key_id_1, saead_name_1},
141        {key_id_2, saead_name_2}});
142 
143   for (int pt_size : {0, 1, 10, 100, 10000}) {
144     std::string plaintext = subtle::Random::GetRandomBytes(pt_size);
145     for (std::string aad : {"some_aad", "", "some other aad"}) {
146       SCOPED_TRACE(absl::StrCat("pt_size = ", pt_size,
147                                 ", aad = '", aad, "'"));
148       // Pre-compute ciphertexts. We create one ciphertext for each primitive
149       // in the primitive set, so that we can test decryption with both
150       // the primary primitive, and the non-primary ones.
151       std::vector<std::unique_ptr<InputStream>> ciphertexts;
152       for (const auto& p : *(saead_set->get_raw_primitives().value())) {
153         ciphertexts.push_back(
154             GetCiphertextSource(&(p->get_primitive()), plaintext, aad));
155       }
156       EXPECT_EQ(3, ciphertexts.size());
157 
158       // Check the decryption of each of the pre-computed ciphertexts.
159       for (auto& ct : ciphertexts) {
160         // Wrap the primitive set and test the resulting DecryptingInputStream.
161         auto dec_stream_result =
162             DecryptingInputStream::New(saead_set, std::move(ct), aad);
163         EXPECT_THAT(dec_stream_result, IsOk());
164         std::string decrypted;
165         auto status =
166             ReadFromStream(dec_stream_result.value().get(), &decrypted);
167         EXPECT_THAT(status, IsOk());
168         EXPECT_EQ(plaintext, decrypted);
169       }
170     }
171   }
172 }
173 
TEST(DecryptingInputStreamTest,WrongAssociatedData)174 TEST(DecryptingInputStreamTest, WrongAssociatedData) {
175   uint32_t key_id_0 = 1234543;
176   uint32_t key_id_1 = 726329;
177   uint32_t key_id_2 = 7213743;
178   std::string saead_name_0 = "streaming_aead0";
179   std::string saead_name_1 = "streaming_aead1";
180   std::string saead_name_2 = "streaming_aead2";
181 
182   auto saead_set = GetTestStreamingAeadSet(
183       {{key_id_0, saead_name_0}, {key_id_1, saead_name_1},
184        {key_id_2, saead_name_2}});
185 
186   for (int pt_size : {0, 1, 10, 100, 10000}) {
187     std::string plaintext = subtle::Random::GetRandomBytes(pt_size);
188     for (std::string aad : {"some_aad", "", "some other aad"}) {
189       SCOPED_TRACE(absl::StrCat("pt_size = ", pt_size,
190                                 ", aad = '", aad, "'"));
191       // Compute a ciphertext with the primary primitive.
192       auto ct = GetCiphertextSource(
193           &(saead_set->get_primary()->get_primitive()), plaintext, aad);
194       auto dec_stream_result =
195           DecryptingInputStream::New(saead_set, std::move(ct), "wrong aad");
196       EXPECT_THAT(dec_stream_result, IsOk());
197       std::string decrypted;
198       auto status = ReadFromStream(dec_stream_result.value().get(), &decrypted);
199       EXPECT_THAT(status, StatusIs(absl::StatusCode::kInvalidArgument));
200     }
201   }
202 }
203 
TEST(DecryptingInputStreamTest,WrongCiphertext)204 TEST(DecryptingInputStreamTest, WrongCiphertext) {
205   uint32_t key_id_0 = 1234543;
206   uint32_t key_id_1 = 726329;
207   uint32_t key_id_2 = 7213743;
208   std::string saead_name_0 = "streaming_aead0";
209   std::string saead_name_1 = "streaming_aead1";
210   std::string saead_name_2 = "streaming_aead2";
211 
212   auto saead_set = GetTestStreamingAeadSet(
213       {{key_id_0, saead_name_0}, {key_id_1, saead_name_1},
214        {key_id_2, saead_name_2}});
215 
216   for (int pt_size : {0, 1, 10, 100, 10000}) {
217     std::string plaintext = subtle::Random::GetRandomBytes(pt_size);
218     for (std::string aad : {"some_aad", "", "some other aad"}) {
219       SCOPED_TRACE(absl::StrCat("pt_size = ", pt_size,
220                                 ", aad = '", aad, "'"));
221       // Try decrypting a wrong ciphertext.
222       auto wrong_ct = GetInputStream(subtle::Random::GetRandomBytes(pt_size));
223       auto dec_stream_result =
224           DecryptingInputStream::New(saead_set, std::move(wrong_ct), aad);
225       EXPECT_THAT(dec_stream_result, IsOk());
226       std::string decrypted;
227       auto status = ReadFromStream(dec_stream_result.value().get(), &decrypted);
228       EXPECT_THAT(status, StatusIs(absl::StatusCode::kInvalidArgument));
229     }
230   }
231 }
232 
233 
234 }  // namespace
235 }  // namespace streamingaead
236 }  // namespace tink
237 }  // namespace crypto
238