| // Copyright 2021 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| #include "tink/internal/aes_util.h" |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <string> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/escaping.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/types/span.h" |
| #include "openssl/aes.h" |
| #include "openssl/evp.h" |
| #include "tink/subtle/subtle_util.h" |
| #include "tink/util/secret_data.h" |
| #include "tink/util/statusor.h" |
| #include "tink/util/test_matchers.h" |
| |
| namespace crypto { |
| namespace tink { |
| namespace internal { |
| namespace { |
| |
| using ::crypto::tink::test::IsOk; |
| using ::crypto::tink::test::IsOkAndHolds; |
| using ::crypto::tink::test::StatusIs; |
| using ::testing::HasSubstr; |
| using ::testing::Not; |
| |
| struct NistAesCtrTestVector { |
| std::string iv; |
| std::string key; |
| std::string plaintext; |
| std::string ciphertext; |
| }; |
| |
| class AesCtrTest : public testing::Test { |
| protected: |
| AesCtrTest() : aes_key_(util::MakeSecretUniquePtr<AES_KEY>()) {} |
| |
| void SetUp() override { |
| ASSERT_EQ(AES_set_encrypt_key( |
| reinterpret_cast<const uint8_t*>(test_vector_.key.data()), |
| /*bits=*/test_vector_.key.size() * 8, aes_key_.get()), |
| 0); |
| } |
| |
| // Test vector from NIST SP 800-38A. |
| NistAesCtrTestVector test_vector_ = { |
| /*iv=*/absl::HexStringToBytes("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"), |
| /*key=*/absl::HexStringToBytes("2b7e151628aed2a6abf7158809cf4f3c"), |
| /*plaintext=*/ |
| absl::HexStringToBytes( |
| "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c8" |
| "1c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"), |
| /*ciphertext=*/ |
| absl::HexStringToBytes( |
| "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4" |
| "df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee"), |
| }; |
| const util::SecretUniquePtr<AES_KEY> aes_key_; |
| }; |
| |
| // Check that AesCtr128Crypt fails when out buffer is too small. |
| TEST_F(AesCtrTest, AesCtrInvalidOutSize) { |
| std::string out; |
| for (int size = 0; size < test_vector_.plaintext.size(); size++) { |
| subtle::ResizeStringUninitialized(&out, size); |
| EXPECT_THAT(AesCtr128Crypt(test_vector_.plaintext, |
| reinterpret_cast<uint8_t*>(&test_vector_.iv[0]), |
| aes_key_.get(), absl::MakeSpan(out)), |
| Not(IsOk())); |
| } |
| } |
| |
| // Partial overlap of buffers of the right size is not allowed. |
| TEST_F(AesCtrTest, AesCtrPartiallyOverlappingFails) { |
| std::string out; |
| subtle::ResizeStringUninitialized(&out, 2 * test_vector_.plaintext.size()); |
| const int kStartIndex = test_vector_.plaintext.size() / 2; |
| std::copy(test_vector_.plaintext.begin(), test_vector_.plaintext.end(), |
| out.begin() + kStartIndex); |
| auto plaintext = |
| absl::string_view(out).substr(kStartIndex, test_vector_.plaintext.size()); |
| util::Status res = AesCtr128Crypt( |
| plaintext, reinterpret_cast<uint8_t*>(&test_vector_.iv[0]), |
| aes_key_.get(), absl::MakeSpan(out).subspan(0, plaintext.size())); |
| // Checking the message to disambiguate from the kInvalidArgumentError that is |
| // returned in case of wrong size of the output buffer. |
| EXPECT_THAT( |
| res, StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("overlap"))); |
| res = AesCtr128Crypt( |
| plaintext, reinterpret_cast<uint8_t*>(&test_vector_.iv[0]), |
| aes_key_.get(), |
| absl::MakeSpan(out).subspan(plaintext.size(), plaintext.size())); |
| EXPECT_THAT( |
| res, StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("overlap"))); |
| } |
| |
| TEST_F(AesCtrTest, AesCtrEncrypt) { |
| std::string out; |
| subtle::ResizeStringUninitialized(&out, test_vector_.plaintext.size()); |
| ASSERT_THAT(AesCtr128Crypt(test_vector_.plaintext, |
| reinterpret_cast<uint8_t*>(&test_vector_.iv[0]), |
| aes_key_.get(), absl::MakeSpan(out)), |
| IsOk()); |
| EXPECT_EQ(out, test_vector_.ciphertext); |
| } |
| |
| TEST_F(AesCtrTest, AesCtrEncryptInPlace) { |
| std::string inout = test_vector_.plaintext; |
| ASSERT_THAT( |
| AesCtr128Crypt(inout, reinterpret_cast<uint8_t*>(&test_vector_.iv[0]), |
| aes_key_.get(), absl::MakeSpan(inout)), |
| IsOk()); |
| EXPECT_EQ(inout, test_vector_.ciphertext); |
| } |
| |
| TEST_F(AesCtrTest, AesCtrDecrypt) { |
| std::string out; |
| subtle::ResizeStringUninitialized(&out, test_vector_.ciphertext.size()); |
| ASSERT_THAT(AesCtr128Crypt(test_vector_.ciphertext, |
| reinterpret_cast<uint8_t*>(&test_vector_.iv[0]), |
| aes_key_.get(), absl::MakeSpan(out)), |
| IsOk()); |
| EXPECT_EQ(out, test_vector_.plaintext); |
| } |
| |
| TEST_F(AesCtrTest, AesCtrDecryptInPlace) { |
| std::string inout = test_vector_.ciphertext; |
| ASSERT_THAT( |
| AesCtr128Crypt(inout, reinterpret_cast<uint8_t*>(&test_vector_.iv[0]), |
| aes_key_.get(), absl::MakeSpan(inout)), |
| IsOk()); |
| EXPECT_EQ(inout, test_vector_.plaintext); |
| } |
| |
| TEST(AesUtilTest, GetAesCtrCipherForKeySize) { |
| for (int i = 0; i < 64; i++) { |
| util::StatusOr<const EVP_CIPHER*> cipher = GetAesCtrCipherForKeySize(i); |
| if (i == 16) { |
| EXPECT_THAT(cipher, IsOkAndHolds(EVP_aes_128_ctr())); |
| } else if (i == 32) { |
| EXPECT_THAT(cipher, IsOkAndHolds(EVP_aes_256_ctr())); |
| } else { |
| EXPECT_THAT(cipher, Not(IsOk())); |
| } |
| } |
| } |
| |
| TEST(AesUtilTest, GetAesCbcCipherForKeySize) { |
| for (int i = 0; i < 64; i++) { |
| util::StatusOr<const EVP_CIPHER*> cipher = GetAesCbcCipherForKeySize(i); |
| if (i == 16) { |
| EXPECT_THAT(cipher, IsOkAndHolds(EVP_aes_128_cbc())); |
| } else if (i == 32) { |
| EXPECT_THAT(cipher, IsOkAndHolds(EVP_aes_256_cbc())); |
| } else { |
| EXPECT_THAT(cipher, Not(IsOk())); |
| } |
| } |
| } |
| |
| } // namespace |
| } // namespace internal |
| } // namespace tink |
| } // namespace crypto |