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/subtle/aes_gcm_hkdf_stream_segment_decrypter.h"
18
19 #include <memory>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #include "gtest/gtest.h"
25 #include "absl/strings/str_cat.h"
26 #include "tink/subtle/aes_gcm_hkdf_stream_segment_encrypter.h"
27 #include "tink/subtle/common_enums.h"
28 #include "tink/subtle/hkdf.h"
29 #include "tink/subtle/random.h"
30 #include "tink/subtle/stream_segment_encrypter.h"
31 #include "tink/util/status.h"
32 #include "tink/util/statusor.h"
33 #include "tink/util/test_util.h"
34
35 namespace crypto {
36 namespace tink {
37 namespace subtle {
38 namespace {
39
GetEncrypter(const util::SecretData & ikm,HashType hkdf_hash,int derived_key_size,int ciphertext_offset,int ciphertext_segment_size,absl::string_view associated_data)40 util::StatusOr<std::unique_ptr<StreamSegmentEncrypter>> GetEncrypter(
41 const util::SecretData& ikm, HashType hkdf_hash, int derived_key_size,
42 int ciphertext_offset, int ciphertext_segment_size,
43 absl::string_view associated_data) {
44 AesGcmHkdfStreamSegmentEncrypter::Params params;
45 params.salt = Random::GetRandomBytes(derived_key_size);
46 auto hkdf_result = Hkdf::ComputeHkdf(
47 hkdf_hash, ikm, params.salt, associated_data,
48 derived_key_size);
49 if (!hkdf_result.ok()) return hkdf_result.status();
50 params.key = hkdf_result.value();
51 params.ciphertext_offset = ciphertext_offset;
52 params.ciphertext_segment_size = ciphertext_segment_size;
53 return AesGcmHkdfStreamSegmentEncrypter::New(params);
54 }
55
TEST(AesGcmHkdfStreamSegmentDecrypterTest,testBasic)56 TEST(AesGcmHkdfStreamSegmentDecrypterTest, testBasic) {
57 for (int ikm_size : {16, 32}) {
58 for (HashType hkdf_hash : {SHA1, SHA256, SHA512}) {
59 for (int derived_key_size = 16;
60 derived_key_size <= ikm_size;
61 derived_key_size += 16) {
62 for (int ciphertext_offset : {0, 5, 10}) {
63 for (int ct_segment_size : {80, 128, 200}) {
64 for (std::string associated_data : {"associated data", "42", ""}) {
65 SCOPED_TRACE(absl::StrCat(
66 "hkdf_hash = ", EnumToString(hkdf_hash),
67 ", ikm_size = ", ikm_size,
68 ", associated_data = '", associated_data, "'",
69 ", derived_key_size = ", derived_key_size,
70 ", ciphertext_offset = ", ciphertext_offset,
71 ", ciphertext_segment_size = ", ct_segment_size));
72
73 // Construct a decrypter.
74 AesGcmHkdfStreamSegmentDecrypter::Params params;
75 params.ikm = Random::GetRandomKeyBytes(ikm_size);
76 params.hkdf_hash = hkdf_hash;
77 params.derived_key_size = derived_key_size;
78 params.ciphertext_offset = ciphertext_offset;
79 params.ciphertext_segment_size = ct_segment_size;
80 params.associated_data = associated_data;
81 auto result = AesGcmHkdfStreamSegmentDecrypter::New(params);
82 EXPECT_TRUE(result.ok()) << result.status();
83 auto dec = std::move(result.value());
84
85 // Try to use the decrypter.
86 std::vector<uint8_t> pt;
87 auto status = dec->DecryptSegment(pt, 42, false, nullptr);
88 EXPECT_FALSE(status.ok());
89 EXPECT_EQ(absl::StatusCode::kFailedPrecondition, status.code());
90 EXPECT_PRED_FORMAT2(testing::IsSubstring, "not initialized",
91 std::string(status.message()));
92
93 // Get an encrypter and initialize the decrypter.
94 auto enc =
95 std::move(GetEncrypter(params.ikm, hkdf_hash,
96 derived_key_size, ciphertext_offset,
97 ct_segment_size, associated_data)
98 .value());
99 status = dec->Init(enc->get_header());
100 EXPECT_TRUE(status.ok()) << status;
101
102 // Use the constructed decrypter.
103 int header_size =
104 derived_key_size + /* nonce_prefix_size = */ 7 + 1;
105 EXPECT_EQ(header_size, dec->get_header_size());
106 EXPECT_EQ(enc->get_header().size(), dec->get_header_size());
107 EXPECT_EQ(ct_segment_size, dec->get_ciphertext_segment_size());
108 EXPECT_EQ(ct_segment_size - /* tag_size = */ 16,
109 dec->get_plaintext_segment_size());
110 EXPECT_EQ(ciphertext_offset, dec->get_ciphertext_offset());
111 int segment_number = 0;
112 for (int pt_size : {1, 10, dec->get_plaintext_segment_size()}) {
113 for (bool is_last_segment : {false, true}) {
114 SCOPED_TRACE(absl::StrCat(
115 "plaintext_size = ", pt_size,
116 ", is_last_segment = ", is_last_segment));
117 std::vector<uint8_t> pt(pt_size, 'p');
118 std::vector<uint8_t> ct;
119 std::vector<uint8_t> decrypted;
120 auto status = enc->EncryptSegment(pt, is_last_segment, &ct);
121 EXPECT_TRUE(status.ok()) << status;
122 status = dec->DecryptSegment(ct, segment_number,
123 is_last_segment, &decrypted);
124 EXPECT_TRUE(status.ok()) << status;
125 EXPECT_EQ(pt, decrypted);
126 segment_number++;
127 EXPECT_EQ(segment_number, enc->get_segment_number());
128 }
129 }
130
131 // Try decryption with wrong params.
132 std::vector<uint8_t> ct(
133 dec->get_ciphertext_segment_size() + 1, 'c');
134 status = dec->DecryptSegment(ct, 42, true, nullptr);
135 EXPECT_FALSE(status.ok());
136 EXPECT_PRED_FORMAT2(testing::IsSubstring, "ciphertext too long",
137 std::string(status.message()));
138 ct.resize(dec->get_plaintext_segment_size());
139 status = dec->DecryptSegment(ct, 42, true, nullptr);
140 EXPECT_FALSE(status.ok());
141 EXPECT_PRED_FORMAT2(testing::IsSubstring, "must be non-null",
142 std::string(status.message()));
143 }
144 }
145 }
146 }
147 }
148 }
149 }
150
151
TEST(AesGcmHkdfStreamSegmentDecrypterTest,testWrongDerivedKeySize)152 TEST(AesGcmHkdfStreamSegmentDecrypterTest, testWrongDerivedKeySize) {
153 for (int derived_key_size : {12, 24, 64}) {
154 for (HashType hkdf_hash : {SHA1, SHA256, SHA512}) {
155 for (int ct_segment_size : {128, 200}) {
156 SCOPED_TRACE(absl::StrCat(
157 "derived_key_size = ", derived_key_size,
158 ", hkdf_hash = ", EnumToString(hkdf_hash),
159 ", ciphertext_segment_size = ", ct_segment_size));
160 AesGcmHkdfStreamSegmentDecrypter::Params params;
161 params.ikm = Random::GetRandomKeyBytes(derived_key_size);
162 params.hkdf_hash = hkdf_hash;
163 params.derived_key_size = derived_key_size;
164 params.ciphertext_offset = 0;
165 params.ciphertext_segment_size = ct_segment_size;
166 params.associated_data = "associated data";
167 auto result = AesGcmHkdfStreamSegmentDecrypter::New(params);
168 EXPECT_FALSE(result.ok());
169 EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
170 EXPECT_PRED_FORMAT2(testing::IsSubstring, "must be 16 or 32",
171 std::string(result.status().message()));
172 }
173 }
174 }
175 }
176
TEST(AesGcmHkdfStreamSegmentDecrypterTest,testWrongIkmSize)177 TEST(AesGcmHkdfStreamSegmentDecrypterTest, testWrongIkmSize) {
178 for (int derived_key_size : {16, 32}) {
179 for (HashType hkdf_hash : {SHA1, SHA256, SHA512}) {
180 for (int ikm_size_delta : {-8, -4, -2, -1}) {
181 SCOPED_TRACE(absl::StrCat(
182 "derived_key_size = ", derived_key_size,
183 ", hkdf_hash = ", EnumToString(hkdf_hash),
184 ", ikm_size_delta = ", ikm_size_delta));
185 AesGcmHkdfStreamSegmentDecrypter::Params params;
186 params.ikm =
187 Random::GetRandomKeyBytes(derived_key_size + ikm_size_delta);
188 params.hkdf_hash = hkdf_hash;
189 params.derived_key_size = derived_key_size;
190 params.ciphertext_offset = 0;
191 params.ciphertext_segment_size = 128;
192 params.associated_data = "associated data";
193 auto result = AesGcmHkdfStreamSegmentDecrypter::New(params);
194 EXPECT_FALSE(result.ok());
195 EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
196 EXPECT_PRED_FORMAT2(testing::IsSubstring, "ikm too small",
197 std::string(result.status().message()));
198 }
199 }
200 }
201 }
202
TEST(AesGcmHkdfStreamSegmentDecrypterTest,testWrongCiphertextOffset)203 TEST(AesGcmHkdfStreamSegmentDecrypterTest, testWrongCiphertextOffset) {
204 for (int derived_key_size : {16, 32}) {
205 for (HashType hkdf_hash : {SHA1, SHA256, SHA512}) {
206 for (int ciphertext_offset : {-16, -10, -3, -1}) {
207 SCOPED_TRACE(absl::StrCat(
208 "derived_key_size = ", derived_key_size,
209 ", hkdf_hash = ", EnumToString(hkdf_hash),
210 ", ciphertext_offset = ", ciphertext_offset));
211 AesGcmHkdfStreamSegmentDecrypter::Params params;
212 params.ikm = Random::GetRandomKeyBytes(derived_key_size);
213 params.hkdf_hash = hkdf_hash;
214 params.derived_key_size = derived_key_size;
215 params.ciphertext_offset = ciphertext_offset;
216 params.ciphertext_segment_size = 128;
217 params.associated_data = "associated data";
218 auto result = AesGcmHkdfStreamSegmentDecrypter::New(params);
219 EXPECT_FALSE(result.ok());
220 EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
221 EXPECT_PRED_FORMAT2(testing::IsSubstring, "must be non-negative",
222 std::string(result.status().message()));
223 }
224 }
225 }
226 }
227
TEST(AesGcmHkdfStreamSegmentDecrypterTest,testWrongCiphertextSegmentSize)228 TEST(AesGcmHkdfStreamSegmentDecrypterTest, testWrongCiphertextSegmentSize) {
229 for (int derived_key_size : {16, 32}) {
230 for (HashType hkdf_hash : {SHA1, SHA256, SHA512}) {
231 for (int ciphertext_offset : {0, 1, 5, 10}) {
232 int min_ct_segment_size = derived_key_size + ciphertext_offset +
233 8 + // nonce_prefix_size + 1
234 16 + // tag_size
235 1;
236
237 for (int ct_segment_size : {min_ct_segment_size - 5,
238 min_ct_segment_size - 1, min_ct_segment_size,
239 min_ct_segment_size + 1, min_ct_segment_size + 10}) {
240 SCOPED_TRACE(absl::StrCat(
241 "derived_key_size = ", derived_key_size,
242 ", ciphertext_offset = ", ciphertext_offset,
243 ", ciphertext_segment_size = ", ct_segment_size));
244 AesGcmHkdfStreamSegmentDecrypter::Params params;
245 params.ikm = Random::GetRandomKeyBytes(derived_key_size);
246 params.hkdf_hash = hkdf_hash;
247 params.derived_key_size = derived_key_size;
248 params.ciphertext_offset = ciphertext_offset;
249 params.ciphertext_segment_size = ct_segment_size;
250 auto result = AesGcmHkdfStreamSegmentDecrypter::New(params);
251 if (ct_segment_size < min_ct_segment_size) {
252 EXPECT_FALSE(result.ok());
253 EXPECT_EQ(absl::StatusCode::kInvalidArgument,
254 result.status().code());
255 EXPECT_PRED_FORMAT2(testing::IsSubstring, "too small",
256 std::string(result.status().message()));
257 } else {
258 EXPECT_TRUE(result.ok()) << result.status();
259 }
260 }
261 }
262 }
263 }
264 }
265
266 } // namespace
267 } // namespace subtle
268 } // namespace tink
269 } // namespace crypto
270