xref: /aosp_15_r20/external/tink/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter_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/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