xref: /aosp_15_r20/external/tink/cc/aead/kms_envelope_aead_test.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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