1 // Copyright 2019 Google LLC
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/aead/kms_envelope_aead.h"
18
19 #include <stdint.h>
20
21 #include <memory>
22 #include <string>
23 #include <utility>
24 #include <vector>
25
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 #include "absl/base/internal/endian.h"
29 #include "absl/memory/memory.h"
30 #include "absl/status/status.h"
31 #include "absl/strings/string_view.h"
32 #include "tink/aead.h"
33 #include "tink/aead/aead_config.h"
34 #include "tink/aead/aead_key_templates.h"
35 #include "tink/internal/ssl_util.h"
36 #include "tink/keyset_handle.h"
37 #include "tink/mac/mac_key_templates.h"
38 #include "tink/registry.h"
39 #include "tink/util/fake_kms_client.h"
40 #include "tink/util/status.h"
41 #include "tink/util/statusor.h"
42 #include "tink/util/test_matchers.h"
43 #include "tink/util/test_util.h"
44 #include "proto/aes_gcm.pb.h"
45
46 namespace crypto {
47 namespace tink {
48 namespace {
49
50 using ::crypto::tink::Aead;
51 using ::crypto::tink::test::DummyAead;
52 using ::crypto::tink::test::IsOk;
53 using ::crypto::tink::test::IsOkAndHolds;
54 using ::crypto::tink::test::StatusIs;
55 using ::google::crypto::tink::KeyTemplate;
56 using ::testing::Eq;
57 using ::testing::HasSubstr;
58 using ::testing::Not;
59 using ::testing::SizeIs;
60 using ::testing::Test;
61
62 constexpr int kEncryptedDekPrefixSize = 4;
63 constexpr absl::string_view kRemoteAeadName = "kms-backed-aead";
64
65 class KmsEnvelopeAeadTest : public Test {
66 protected:
TearDown()67 void TearDown() override { Registry::Reset(); }
68 };
69
TEST_F(KmsEnvelopeAeadTest,EncryptDecryptSucceed)70 TEST_F(KmsEnvelopeAeadTest, EncryptDecryptSucceed) {
71 ASSERT_THAT(AeadConfig::Register(), IsOk());
72
73 // Use an AES-128-GCM primitive as the remote one.
74 util::StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle =
75 KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm());
76 ASSERT_THAT(keyset_handle, IsOk());
77 KeyTemplate dek_template = AeadKeyTemplates::Aes128Eax();
78 util::StatusOr<std::unique_ptr<Aead>> remote_aead =
79 (*keyset_handle)->GetPrimitive<Aead>();
80
81 util::StatusOr<std::unique_ptr<Aead>> envelope_aead =
82 KmsEnvelopeAead::New(dek_template, *std::move(remote_aead));
83 ASSERT_THAT(envelope_aead, IsOk());
84
85 std::string message = "Some data to encrypt.";
86 std::string aad = "Some associated data.";
87 util::StatusOr<std::string> encrypt_result =
88 (*envelope_aead)->Encrypt(message, aad);
89 ASSERT_THAT(encrypt_result, IsOk());
90 util::StatusOr<std::string> decrypt_result =
91 (*envelope_aead)->Decrypt(encrypt_result.value(), aad);
92 EXPECT_THAT(decrypt_result, IsOkAndHolds(message));
93 }
94
TEST_F(KmsEnvelopeAeadTest,NewFailsIfReamoteAeadIsNull)95 TEST_F(KmsEnvelopeAeadTest, NewFailsIfReamoteAeadIsNull) {
96 KeyTemplate dek_template = AeadKeyTemplates::Aes128Eax();
97 EXPECT_THAT(
98 KmsEnvelopeAead::New(dek_template, /*remote_aead=*/nullptr).status(),
99 StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("non-null")));
100 }
101
TEST_F(KmsEnvelopeAeadTest,NewFailsIfDekKeyManagerIsNotRegistered)102 TEST_F(KmsEnvelopeAeadTest, NewFailsIfDekKeyManagerIsNotRegistered) {
103 KeyTemplate dek_template = AeadKeyTemplates::Aes128Eax();
104 auto remote_aead = absl::make_unique<DummyAead>(kRemoteAeadName);
105 EXPECT_THAT(
106 KmsEnvelopeAead::New(dek_template, std::move(remote_aead)).status(),
107 StatusIs(absl::StatusCode::kNotFound, HasSubstr("AesEaxKey")));
108 }
109
TEST_F(KmsEnvelopeAeadTest,NewFailsIfUsingDekTemplateOfUnsupportedKeyType)110 TEST_F(KmsEnvelopeAeadTest, NewFailsIfUsingDekTemplateOfUnsupportedKeyType) {
111 ASSERT_THAT(AeadConfig::Register(), IsOk());
112 KeyTemplate dek_template = MacKeyTemplates::HmacSha256();
113 auto remote_aead = absl::make_unique<DummyAead>(kRemoteAeadName);
114 EXPECT_THAT(
115 KmsEnvelopeAead::New(dek_template, std::move(remote_aead)).status(),
116 StatusIs(absl::StatusCode::kInvalidArgument,
117 HasSubstr("unsupported key type")));
118 }
119
TEST_F(KmsEnvelopeAeadTest,DecryptFailsWithInvalidCiphertextOrAad)120 TEST_F(KmsEnvelopeAeadTest, DecryptFailsWithInvalidCiphertextOrAad) {
121 ASSERT_THAT(AeadConfig::Register(), IsOk());
122
123 KeyTemplate dek_template = AeadKeyTemplates::Aes128Gcm();
124 auto remote_aead = absl::make_unique<DummyAead>(kRemoteAeadName);
125 util::StatusOr<std::unique_ptr<Aead>> aead =
126 KmsEnvelopeAead::New(dek_template, std::move(remote_aead));
127 ASSERT_THAT(aead, IsOk());
128
129 std::string message = "Some data to encrypt.";
130 std::string aad = "Some associated data.";
131 util::StatusOr<std::string> encrypt_result = (*aead)->Encrypt(message, aad);
132 ASSERT_THAT(encrypt_result, IsOk());
133 auto ciphertext = absl::string_view(*encrypt_result);
134
135 // Ciphertext has size zero or smaller than 4 bytes.
136 EXPECT_THAT(
137 (*aead)->Decrypt(/*ciphertext=*/"", aad).status(),
138 StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("too short")));
139 EXPECT_THAT(
140 (*aead)->Decrypt(/*ciphertext=*/"sh", aad).status(),
141 StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("too short")));
142
143 // Ciphertext is smaller than the size of the key.
144 const int dek_encrypted_key_size = absl::big_endian::Load32(
145 reinterpret_cast<const uint8_t*>(ciphertext.data()));
146 // We leave only key size and key truncated by one.
147 EXPECT_THAT(
148 (*aead)
149 ->Decrypt(ciphertext.substr(0, 4 + dek_encrypted_key_size - 1), aad)
150 .status(),
151 StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("invalid")));
152
153 std::string corrupted_ciphertext = *encrypt_result;
154 // Corrupt the serialized DEK.
155 corrupted_ciphertext[4] = 'a';
156 EXPECT_THAT(
157 (*aead)->Decrypt(corrupted_ciphertext, aad).status(),
158 StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("invalid")));
159
160 // Wrong associated data.
161 EXPECT_THAT((*aead)->Decrypt(ciphertext, "wrong aad").status(),
162 StatusIs(absl::StatusCode::kInternal,
163 HasSubstr("Authentication failed")));
164 }
165
TEST_F(KmsEnvelopeAeadTest,DekMaintainsCorrectKeyFormat)166 TEST_F(KmsEnvelopeAeadTest, DekMaintainsCorrectKeyFormat) {
167 ASSERT_THAT(AeadConfig::Register(), IsOk());
168
169 KeyTemplate dek_template = AeadKeyTemplates::Aes128Gcm();
170 auto kms_remote_aead = absl::make_unique<DummyAead>(kRemoteAeadName);
171 util::StatusOr<std::unique_ptr<Aead>> aead =
172 KmsEnvelopeAead::New(dek_template, std::move(kms_remote_aead));
173 ASSERT_THAT(aead, IsOk());
174
175 std::string message = "Some data to encrypt.";
176 std::string aad = "Some associated data.";
177 util::StatusOr<std::string> ciphertext = (*aead)->Encrypt(message, aad);
178 ASSERT_THAT(ciphertext, IsOk());
179
180 // Recover DEK from ciphertext (see
181 // https://developers.google.com/tink/wire-format#envelope_encryption).
182 auto enc_dek_size = absl::big_endian::Load32(
183 reinterpret_cast<const uint8_t*>(ciphertext->data()));
184 DummyAead remote_aead = DummyAead(kRemoteAeadName);
185 absl::string_view encrypted_dek =
186 absl::string_view(*ciphertext)
187 .substr(kEncryptedDekPrefixSize, enc_dek_size);
188 util::StatusOr<std::string> dek_proto_bytes =
189 remote_aead.Decrypt(encrypted_dek,
190 /*associated_data=*/"");
191 ASSERT_THAT(dek_proto_bytes, IsOk());
192
193 // Check if we can deserialize a GCM key proto from the decrypted DEK.
194 google::crypto::tink::AesGcmKey key;
195 EXPECT_TRUE(key.ParseFromString(dek_proto_bytes.value()));
196 EXPECT_THAT(key.key_value(), SizeIs(16));
197 }
198
TEST_F(KmsEnvelopeAeadTest,MultipleEncryptionsProduceDifferentDeks)199 TEST_F(KmsEnvelopeAeadTest, MultipleEncryptionsProduceDifferentDeks) {
200 ASSERT_THAT(AeadConfig::Register(), IsOk());
201
202 KeyTemplate dek_template = AeadKeyTemplates::Aes128Gcm();
203 auto kms_remote_aead = absl::make_unique<DummyAead>(kRemoteAeadName);
204 util::StatusOr<std::unique_ptr<Aead>> aead =
205 KmsEnvelopeAead::New(dek_template, std::move(kms_remote_aead));
206 ASSERT_THAT(aead, IsOk());
207
208 std::string message = "Some data to encrypt.";
209 std::string aad = "Some associated data.";
210
211 constexpr int kNumIterations = 2;
212 std::vector<google::crypto::tink::AesGcmKey> ciphertexts;
213 ciphertexts.reserve(kNumIterations);
214 for (int i = 0; i < kNumIterations; i++) {
215 util::StatusOr<std::string> ciphertext = (*aead)->Encrypt(message, aad);
216 ASSERT_THAT(ciphertext, IsOk());
217
218 auto enc_dek_size = absl::big_endian::Load32(
219 reinterpret_cast<const uint8_t*>(ciphertext->data()));
220 DummyAead remote_aead = DummyAead(kRemoteAeadName);
221 util::StatusOr<std::string> dek_proto_bytes = remote_aead.Decrypt(
222 ciphertext->substr(kEncryptedDekPrefixSize, enc_dek_size),
223 /*associated_data=*/"");
224 ASSERT_THAT(dek_proto_bytes, IsOk());
225
226 google::crypto::tink::AesGcmKey key;
227 ASSERT_TRUE(key.ParseFromString(dek_proto_bytes.value()));
228 ASSERT_THAT(key.key_value(), SizeIs(16));
229 ciphertexts.push_back(key);
230 }
231
232 for (int i = 0; i < ciphertexts.size() - 1; i++) {
233 for (int j = i + 1; j < ciphertexts.size(); j++) {
234 EXPECT_THAT(ciphertexts[i].SerializeAsString(),
235 Not(Eq(ciphertexts[j].SerializeAsString())));
236 }
237 }
238 }
239
240 class KmsEnvelopeAeadDekTemplatesTest
241 : public testing::TestWithParam<KeyTemplate> {
SetUp()242 void SetUp() override { ASSERT_THAT(AeadConfig::Register(), IsOk()); }
243 };
244
TEST_P(KmsEnvelopeAeadDekTemplatesTest,EncryptDecrypt)245 TEST_P(KmsEnvelopeAeadDekTemplatesTest, EncryptDecrypt) {
246 // Use an AES-128-GCM primitive as the remote AEAD.
247 util::StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle =
248 KeysetHandle::GenerateNew(AeadKeyTemplates::Aes128Gcm());
249 ASSERT_THAT(keyset_handle, IsOk());
250 util::StatusOr<std::unique_ptr<Aead>> remote_aead =
251 (*keyset_handle)->GetPrimitive<Aead>();
252
253 KeyTemplate dek_template = GetParam();
254 util::StatusOr<std::unique_ptr<Aead>> envelope_aead =
255 KmsEnvelopeAead::New(dek_template, *std::move(remote_aead));
256 ASSERT_THAT(envelope_aead, IsOk());
257
258 std::string plaintext = "plaintext";
259 std::string associated_data = "associated_data";
260 util::StatusOr<std::string> ciphertext =
261 (*envelope_aead)->Encrypt(plaintext, associated_data);
262 ASSERT_THAT(ciphertext, IsOk());
263 util::StatusOr<std::string> decrypted =
264 (*envelope_aead)->Decrypt(ciphertext.value(), associated_data);
265 EXPECT_THAT(decrypted, IsOkAndHolds(plaintext));
266 }
267
GetTestTemplates()268 std::vector<KeyTemplate> GetTestTemplates() {
269 std::vector<KeyTemplate> templates = {
270 AeadKeyTemplates::Aes128Gcm(),
271 AeadKeyTemplates::Aes256Gcm(),
272 AeadKeyTemplates::Aes128CtrHmacSha256(),
273 AeadKeyTemplates::Aes128Eax(),
274 AeadKeyTemplates::Aes128GcmNoPrefix()
275 };
276 if (internal::IsBoringSsl()) {
277 templates.push_back(AeadKeyTemplates::XChaCha20Poly1305());
278 templates.push_back(AeadKeyTemplates::Aes256GcmSiv());
279 }
280 return templates;
281 }
282
283 INSTANTIATE_TEST_SUITE_P(
284 KmsEnvelopeAeadDekTemplatesTest, KmsEnvelopeAeadDekTemplatesTest,
285 testing::ValuesIn(GetTestTemplates()));
286
TEST_F(KmsEnvelopeAeadTest,PrimitiveFromTemplateAndFromNewAreCompatible)287 TEST_F(KmsEnvelopeAeadTest, PrimitiveFromTemplateAndFromNewAreCompatible) {
288 ASSERT_THAT(AeadConfig::Register(), IsOk());
289
290 util::StatusOr<std::string> kek_uri_result =
291 test::FakeKmsClient::CreateFakeKeyUri();
292 ASSERT_THAT(kek_uri_result, IsOk());
293 std::string kek_uri = *kek_uri_result;
294 KeyTemplate dek_template = AeadKeyTemplates::Aes128Gcm();
295
296 // Create a KmsEnvelopeAead primitive from a KmsEnvelopeAeadKey template.
297 util::Status register_status =
298 test::FakeKmsClient::RegisterNewClient(kek_uri, /*credentials_path=*/"");
299 ASSERT_THAT(register_status, IsOk());
300 // Create a KmsEnvelopeAeadKey template.
301 KeyTemplate env_template =
302 AeadKeyTemplates::KmsEnvelopeAead(kek_uri, dek_template);
303 // Get KMS envelope AEAD primitive.
304 util::StatusOr<std::unique_ptr<KeysetHandle>> handle =
305 KeysetHandle::GenerateNew(env_template);
306 ASSERT_THAT(handle, IsOk());
307 util::StatusOr<std::unique_ptr<Aead>> envelope_aead_from_template =
308 (*handle)->GetPrimitive<Aead>();
309 ASSERT_THAT(envelope_aead_from_template, IsOk());
310
311 // Create a KmsEnvelopeAead primitive form KmsEnvelopeAead::New.
312 util::StatusOr<std::unique_ptr<test::FakeKmsClient>> client =
313 test::FakeKmsClient::New(/*key_uri=*/"", /*credentials_path=*/"");
314 ASSERT_THAT(client, IsOk());
315 util::StatusOr<std::unique_ptr<Aead>> remote_aead =
316 (*client)->GetAead(kek_uri);
317 ASSERT_THAT(remote_aead, IsOk());
318 // Get KMS envelope AEAD primitive.
319 util::StatusOr<std::unique_ptr<Aead>> envelope_aead_from_new =
320 KmsEnvelopeAead::New(dek_template, *std::move(remote_aead));
321 ASSERT_THAT(envelope_aead_from_new, IsOk());
322
323 // Check that envelope_aead_from_template and envelope_aead_from_new are the
324 // same primitive by encrypting with envelope_aead_from_template and
325 // decrypting with envelope_aead_from_new and vice versa.
326 std::string plaintext = "plaintext";
327 std::string associated_data = "associated_data";
328 {
329 util::StatusOr<std::string> ciphertext =
330 (*envelope_aead_from_template)->Encrypt(plaintext, associated_data);
331 ASSERT_THAT(ciphertext, IsOk());
332 util::StatusOr<std::string> decrypted =
333 (*envelope_aead_from_new)->Decrypt(ciphertext.value(), associated_data);
334 EXPECT_THAT(decrypted, IsOkAndHolds(plaintext));
335 }
336 {
337 util::StatusOr<std::string> ciphertext =
338 (*envelope_aead_from_new)->Encrypt(plaintext, associated_data);
339 ASSERT_THAT(ciphertext, IsOk());
340 util::StatusOr<std::string> decrypted =
341 (*envelope_aead_from_template)
342 ->Decrypt(ciphertext.value(), associated_data);
343 EXPECT_THAT(decrypted, IsOkAndHolds(plaintext));
344 }
345 }
346
347 } // namespace
348 } // namespace tink
349 } // namespace crypto
350