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