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_streaming.h"
18
19 #include <sstream>
20 #include <string>
21 #include <utility>
22
23 #include "gtest/gtest.h"
24 #include "absl/memory/memory.h"
25 #include "absl/status/status.h"
26 #include "absl/strings/str_cat.h"
27 #include "tink/config/tink_fips.h"
28 #include "tink/output_stream.h"
29 #include "tink/subtle/common_enums.h"
30 #include "tink/subtle/random.h"
31 #include "tink/subtle/streaming_aead_test_util.h"
32 #include "tink/subtle/test_util.h"
33 #include "tink/util/istream_input_stream.h"
34 #include "tink/util/ostream_output_stream.h"
35 #include "tink/util/status.h"
36 #include "tink/util/statusor.h"
37 #include "tink/util/test_matchers.h"
38
39 namespace crypto {
40 namespace tink {
41 namespace subtle {
42 namespace {
43
44 using ::crypto::tink::test::IsOk;
45 using ::crypto::tink::test::StatusIs;
46
TEST(AesGcmHkdfStreamingTest,testBasic)47 TEST(AesGcmHkdfStreamingTest, testBasic) {
48 if (IsFipsModeEnabled()) {
49 GTEST_SKIP() << "Not supported in FIPS-only mode";
50 }
51 for (HashType hkdf_hash : {SHA1, SHA256, SHA512}) {
52 for (int ikm_size : {16, 32}) {
53 for (int derived_key_size = 16;
54 derived_key_size <= ikm_size;
55 derived_key_size += 16) {
56 for (int ct_segment_size : {80, 128, 200}) {
57 for (int ciphertext_offset : {0, 10, 16}) {
58 SCOPED_TRACE(absl::StrCat(
59 "hkdf_hash = ", EnumToString(hkdf_hash),
60 ", ikm_size = ", ikm_size,
61 ", derived_key_size = ", derived_key_size,
62 ", ciphertext_segment_size = ", ct_segment_size,
63 ", ciphertext_offset = ", ciphertext_offset));
64 // Create AesGcmHkdfStreaming.
65 AesGcmHkdfStreaming::Params params;
66 params.ikm = Random::GetRandomKeyBytes(ikm_size);
67 params.hkdf_hash = hkdf_hash;
68 params.derived_key_size = derived_key_size;
69 params.ciphertext_segment_size = ct_segment_size;
70 params.ciphertext_offset = ciphertext_offset;
71 auto result = AesGcmHkdfStreaming::New(std::move(params));
72 EXPECT_TRUE(result.ok()) << result.status();
73 auto streaming_aead = std::move(result.value());
74
75 // Try to get an encrypting stream to a "null" ct_destination.
76 std::string associated_data = "some associated data";
77 auto failed_result = streaming_aead->NewEncryptingStream(
78 nullptr, associated_data);
79 EXPECT_FALSE(failed_result.ok());
80 EXPECT_EQ(absl::StatusCode::kInvalidArgument,
81 failed_result.status().code());
82 EXPECT_PRED_FORMAT2(testing::IsSubstring, "non-null",
83 std::string(failed_result.status().message()));
84
85 for (int pt_size : {0, 16, 100, 1000, 10000}) {
86 SCOPED_TRACE(absl::StrCat(" pt_size = ", pt_size));
87 std::string pt = Random::GetRandomBytes(pt_size);
88 EXPECT_THAT(
89 EncryptThenDecrypt(streaming_aead.get(), streaming_aead.get(),
90 pt, associated_data, ciphertext_offset),
91 IsOk());
92 }
93 }
94 }
95 }
96 }
97 }
98 }
99
TEST(AesGcmHkdfStreamingTest,testIkmSmallerThanDerivedKey)100 TEST(AesGcmHkdfStreamingTest, testIkmSmallerThanDerivedKey) {
101 if (IsFipsModeEnabled()) {
102 GTEST_SKIP() << "Not supported in FIPS-only mode";
103 }
104 AesGcmHkdfStreaming::Params params;
105 int ikm_size = 16;
106 params.ikm = Random::GetRandomKeyBytes(ikm_size);
107 params.derived_key_size = 17;
108 params.ciphertext_segment_size = 100;
109 params.ciphertext_offset = 10;
110 params.hkdf_hash = SHA256;
111 auto result = AesGcmHkdfStreaming::New(std::move(params));
112 EXPECT_FALSE(result.ok());
113 EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
114 EXPECT_PRED_FORMAT2(testing::IsSubstring, "ikm too small",
115 std::string(result.status().message()));
116 }
117
TEST(AesGcmHkdfStreamingTest,testIkmSize)118 TEST(AesGcmHkdfStreamingTest, testIkmSize) {
119 if (IsFipsModeEnabled()) {
120 GTEST_SKIP() << "Not supported in FIPS-only mode";
121 }
122 for (int ikm_size : {5, 10, 15}) {
123 AesGcmHkdfStreaming::Params params;
124 params.ikm = Random::GetRandomKeyBytes(ikm_size);
125 params.derived_key_size = 17;
126 params.ciphertext_segment_size = 100;
127 params.ciphertext_offset = 0;
128 params.hkdf_hash = SHA256;
129
130 auto result = AesGcmHkdfStreaming::New(std::move(params));
131 EXPECT_FALSE(result.ok());
132 EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
133 EXPECT_PRED_FORMAT2(testing::IsSubstring, "ikm too small",
134 std::string(result.status().message()));
135 }
136 }
137
TEST(AesGcmHkdfStreamingTest,testWrongHkdfHash)138 TEST(AesGcmHkdfStreamingTest, testWrongHkdfHash) {
139 if (IsFipsModeEnabled()) {
140 GTEST_SKIP() << "Not supported in FIPS-only mode";
141 }
142 AesGcmHkdfStreaming::Params params;
143 int ikm_size = 16;
144 params.ikm = Random::GetRandomKeyBytes(ikm_size);
145 params.derived_key_size = 16;
146 params.ciphertext_segment_size = 100;
147 params.ciphertext_offset = 10;
148 params.hkdf_hash = SHA384;
149
150 auto result = AesGcmHkdfStreaming::New(std::move(params));
151 EXPECT_FALSE(result.ok());
152 EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
153 EXPECT_PRED_FORMAT2(testing::IsSubstring, "unsupported hkdf_hash",
154 std::string(result.status().message()));
155 }
156
TEST(AesGcmHkdfStreamingTest,testWrongDerivedKeySize)157 TEST(AesGcmHkdfStreamingTest, testWrongDerivedKeySize) {
158 if (IsFipsModeEnabled()) {
159 GTEST_SKIP() << "Not supported in FIPS-only mode";
160 }
161 AesGcmHkdfStreaming::Params params;
162 int ikm_size = 20;
163 params.ikm = Random::GetRandomKeyBytes(ikm_size);
164 params.derived_key_size = 20;
165 params.ciphertext_segment_size = 100;
166 params.ciphertext_offset = 10;
167 params.hkdf_hash = SHA256;
168
169 auto result = AesGcmHkdfStreaming::New(std::move(params));
170 EXPECT_FALSE(result.ok());
171 EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
172 EXPECT_PRED_FORMAT2(testing::IsSubstring, "must be 16 or 32",
173 std::string(result.status().message()));
174 }
175
TEST(AesGcmHkdfStreamingTest,testWrongCiphertextOffset)176 TEST(AesGcmHkdfStreamingTest, testWrongCiphertextOffset) {
177 if (IsFipsModeEnabled()) {
178 GTEST_SKIP() << "Not supported in FIPS-only mode";
179 }
180 AesGcmHkdfStreaming::Params params;
181 int ikm_size = 32;
182 params.ikm = Random::GetRandomKeyBytes(ikm_size);
183 params.derived_key_size = 32;
184 params.ciphertext_segment_size = 100;
185 params.ciphertext_offset = -5;
186 params.hkdf_hash = SHA256;
187
188 auto result = AesGcmHkdfStreaming::New(std::move(params));
189 EXPECT_FALSE(result.ok());
190 EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
191 EXPECT_PRED_FORMAT2(testing::IsSubstring, "must be non-negative",
192 std::string(result.status().message()));
193 }
194
TEST(AesGcmHkdfStreamingTest,testWrongCiphertextSegmentSize)195 TEST(AesGcmHkdfStreamingTest, testWrongCiphertextSegmentSize) {
196 if (IsFipsModeEnabled()) {
197 GTEST_SKIP() << "Not supported in FIPS-only mode";
198 }
199 AesGcmHkdfStreaming::Params params;
200 int ikm_size = 32;
201 params.ikm = Random::GetRandomKeyBytes(ikm_size);
202 params.derived_key_size = 32;
203 params.ciphertext_segment_size = 64;
204 params.ciphertext_offset = 40;
205 params.hkdf_hash = SHA256;
206
207 auto result = AesGcmHkdfStreaming::New(std::move(params));
208 EXPECT_FALSE(result.ok());
209 EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
210 EXPECT_PRED_FORMAT2(testing::IsSubstring, "ciphertext_segment_size too small",
211 std::string(result.status().message()));
212 }
213
214
215 // FIPS only mode tests
TEST(AesGcmHkdfStreamingTest,TestFipsOnly)216 TEST(AesGcmHkdfStreamingTest, TestFipsOnly) {
217 if (!IsFipsModeEnabled()) {
218 GTEST_SKIP() << "Only supported in FIPS-only mode";
219 }
220 AesGcmHkdfStreaming::Params params;
221 int ikm_size = 32;
222 params.ikm = Random::GetRandomKeyBytes(ikm_size);
223 params.derived_key_size = 32;
224 params.ciphertext_segment_size = 64;
225 params.ciphertext_offset = 40;
226 params.hkdf_hash = SHA256;
227
228 EXPECT_THAT(AesGcmHkdfStreaming::New(std::move(params)).status(),
229 StatusIs(absl::StatusCode::kInternal));
230 }
231
232 } // namespace
233 } // namespace subtle
234 } // namespace tink
235 } // namespace crypto
236