blob: 489b7deafdc5664c513e133fbd3ce6b707b9982b [file] [log] [blame]
// 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/aead/internal/zero_copy_aead_wrapper.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "tink/aead/internal/mock_zero_copy_aead.h"
#include "tink/subtle/subtle_util.h"
#include "tink/util/test_matchers.h"
namespace crypto {
namespace tink {
namespace internal {
namespace {
using ::crypto::tink::PrimitiveSet;
using ::crypto::tink::test::IsOk;
using ::crypto::tink::test::StatusIs;
using ::crypto::tink::util::StatusOr;
using ::google::crypto::tink::KeysetInfo;
using ::google::crypto::tink::KeyStatusType;
using ::google::crypto::tink::OutputPrefixType;
using ::testing::_;
using ::testing::HasSubstr;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::Unused;
constexpr absl::string_view kPlaintext = "Some data to encrypt.";
constexpr absl::string_view kAad = "Some data to authenticate.";
constexpr absl::string_view kCiphertext = "iv:Some data to encrypt.:tag";
using ZeroCopyAeadEntry =
crypto::tink::PrimitiveSet<ZeroCopyAead>::Entry<ZeroCopyAead>;
TEST(ZeroCopyAeadWrapperEmptyTest, Nullptr) {
ZeroCopyAeadWrapper wrapper;
StatusOr<std::unique_ptr<Aead>> aead_set = wrapper.Wrap(nullptr);
EXPECT_THAT(aead_set.status(),
StatusIs(absl::StatusCode::kInternal, HasSubstr("non-NULL")));
}
TEST(ZeroCopyAeadWrapperEmptyTest, Empty) {
ZeroCopyAeadWrapper wrapper;
StatusOr<std::unique_ptr<Aead>> aead_set =
wrapper.Wrap(absl::make_unique<PrimitiveSet<ZeroCopyAead>>());
EXPECT_THAT(aead_set.status(), StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("no primary")));
}
class ZeroCopyAeadWrapperTest : public testing::Test {
protected:
void SetUp() override {
// Defines a Tink-type key.
KeysetInfo::KeyInfo key_info;
key_info.set_output_prefix_type(OutputPrefixType::TINK);
key_info.set_key_id(1234543);
key_info.set_status(KeyStatusType::ENABLED);
// Creates a new AEAD set, adds a mock AEAD corresponding to the above key,
// and stores the set as aead_set_.
std::unique_ptr<PrimitiveSet<ZeroCopyAead>> aead_set(
new PrimitiveSet<ZeroCopyAead>());
auto entry = aead_set->AddPrimitive(SetUpMockZeroCopyAead(), key_info);
ASSERT_THAT(entry, IsOk());
ASSERT_THAT(aead_set->set_primary(*entry), IsOk());
aead_set_ = std::move(aead_set);
}
// Returns an AEAD with expected return values for all its functions set via
// EXPECT_CALL. All values are derived from constants kPlaintext, kAad, and
// kCiphertext.
std::unique_ptr<MockZeroCopyAead> SetUpMockZeroCopyAead() {
auto aead = absl::make_unique<MockZeroCopyAead>();
EXPECT_CALL(*aead, MaxEncryptionSize(kPlaintext.size()))
.WillRepeatedly(Return(kCiphertext.size()));
EXPECT_CALL(*aead, Encrypt(kPlaintext, kAad, _))
.WillRepeatedly(Invoke([&](Unused, Unused, absl::Span<char> buffer) {
memcpy(buffer.data(), kCiphertext.data(), kCiphertext.size());
return kCiphertext.size();
}));
EXPECT_CALL(*aead, MaxDecryptionSize(kCiphertext.size()))
.WillRepeatedly(Return(kPlaintext.size()));
EXPECT_CALL(*aead, Decrypt(kCiphertext, kAad, _))
.WillRepeatedly(Invoke([&](Unused, Unused, absl::Span<char> buffer) {
std::memcpy(buffer.data(), kPlaintext.data(), kPlaintext.size());
return kPlaintext.size();
}));
return aead;
}
std::unique_ptr<PrimitiveSet<ZeroCopyAead>> aead_set_;
};
TEST_F(ZeroCopyAeadWrapperTest, EncryptDecrypt) {
ZeroCopyAeadWrapper wrapper;
StatusOr<std::unique_ptr<Aead>> aead_set = wrapper.Wrap(std::move(aead_set_));
ASSERT_THAT(aead_set, IsOk());
StatusOr<std::string> ciphertext = (*aead_set)->Encrypt(kPlaintext, kAad);
ASSERT_THAT(ciphertext, IsOk());
StatusOr<std::string> plaintext = (*aead_set)->Decrypt(*ciphertext, kAad);
ASSERT_THAT(plaintext, IsOk());
EXPECT_EQ(*plaintext, kPlaintext);
}
TEST_F(ZeroCopyAeadWrapperTest, EncryptMultipleKeys) {
// Manually encrypt with the primary key.
ZeroCopyAead& aead = aead_set_->get_primary()->get_primitive();
std::string ciphertext;
subtle::ResizeStringUninitialized(
&ciphertext, CryptoFormat::kNonRawPrefixSize +
aead.MaxEncryptionSize(kPlaintext.size()));
StatusOr<int64_t> ciphertext_size = aead.Encrypt(
kPlaintext, kAad,
absl::MakeSpan(ciphertext)
.subspan(CryptoFormat::kNonRawPrefixSize, ciphertext.size()));
ASSERT_THAT(ciphertext_size, IsOk());
const std::string& key_id = aead_set_->get_primary()->get_identifier();
std::memcpy(&ciphertext[0], key_id.data(), key_id.size());
ciphertext.resize(key_id.size() + *ciphertext_size);
// Add a second key.
KeysetInfo::KeyInfo key_info;
key_info.set_output_prefix_type(OutputPrefixType::TINK);
key_info.set_key_id(42);
key_info.set_status(KeyStatusType::ENABLED);
std::unique_ptr<ZeroCopyAead> aead1 = absl::make_unique<MockZeroCopyAead>();
ASSERT_THAT(aead_set_->AddPrimitive(std::move(aead1), key_info).status(),
IsOk());
ZeroCopyAeadWrapper wrapper;
StatusOr<std::unique_ptr<Aead>> aead_set = wrapper.Wrap(std::move(aead_set_));
ASSERT_THAT(aead_set, IsOk());
// Encrypt with the wrapped AEAD and check that the result is equal to
// encrypting directly with the primary key.
StatusOr<std::string> wrap_ciphertext =
(*aead_set)->Encrypt(kPlaintext, kAad);
ASSERT_THAT(wrap_ciphertext, IsOk());
EXPECT_EQ(*wrap_ciphertext, ciphertext);
}
TEST_F(ZeroCopyAeadWrapperTest, EncryptDecryptRawKey) {
// Add raw key to AEAD set.
KeysetInfo::KeyInfo key_info;
key_info.set_output_prefix_type(OutputPrefixType::RAW);
key_info.set_key_id(1234);
key_info.set_status(KeyStatusType::ENABLED);
auto entry = aead_set_->AddPrimitive(SetUpMockZeroCopyAead(), key_info);
ASSERT_THAT(entry, IsOk());
ASSERT_THAT(aead_set_->set_primary(*entry), IsOk());
// Manually encrypt with the raw key.
util::StatusOr<const std::vector<std::unique_ptr<ZeroCopyAeadEntry>>*>
raw_primitives = aead_set_->get_raw_primitives();
ASSERT_THAT(raw_primitives, IsOk());
EXPECT_EQ((*raw_primitives)->size(), 1);
ZeroCopyAead& aead = (*raw_primitives)->front()->get_primitive();
std::string ciphertext;
subtle::ResizeStringUninitialized(&ciphertext,
aead.MaxEncryptionSize(kPlaintext.size()));
util::StatusOr<int64_t> ciphertext_size =
aead.Encrypt(kPlaintext, kAad, absl::MakeSpan(ciphertext));
ASSERT_THAT(ciphertext_size, IsOk());
ciphertext.resize(*ciphertext_size);
// Encrypt with the wrapped AEAD and check that the result is equal to
// encrypting directly with the raw key.
ZeroCopyAeadWrapper wrapper;
StatusOr<std::unique_ptr<Aead>> aead_set = wrapper.Wrap(std::move(aead_set_));
ASSERT_THAT(aead_set, IsOk());
StatusOr<std::string> wrap_ciphertext =
(*aead_set)->Encrypt(kPlaintext, kAad);
ASSERT_THAT(wrap_ciphertext, IsOk());
EXPECT_EQ(*wrap_ciphertext, ciphertext);
// Manually decrypt with the raw key.
std::string plaintext;
subtle::ResizeStringUninitialized(&plaintext,
aead.MaxDecryptionSize(ciphertext.size()));
util::StatusOr<int64_t> plaintext_size =
aead.Decrypt(ciphertext, kAad, absl::MakeSpan(plaintext));
ASSERT_THAT(plaintext_size, IsOk());
plaintext.resize(*plaintext_size);
EXPECT_EQ(plaintext, kPlaintext);
// Decrypt with the wrapped AEAD.
StatusOr<std::string> wrap_plaintext = (*aead_set)->Decrypt(ciphertext, kAad);
ASSERT_THAT(wrap_plaintext, IsOk());
EXPECT_EQ(*wrap_plaintext, kPlaintext);
}
TEST_F(ZeroCopyAeadWrapperTest, EncryptBadDecrypt) {
ZeroCopyAeadWrapper wrapper;
StatusOr<std::unique_ptr<Aead>> aead_set = wrapper.Wrap(std::move(aead_set_));
ASSERT_THAT(aead_set, IsOk());
StatusOr<std::string> plaintext =
(*aead_set)->Decrypt("some bad ciphertext", kAad);
EXPECT_EQ(plaintext.status().code(), absl::StatusCode::kInvalidArgument);
}
} // namespace
} // namespace internal
} // namespace tink
} // namespace crypto