blob: d231d1c6b09bfdc4215524fa603889b7cf692bb9 [file] [log] [blame]
// Copyright 2022 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/mac/internal/chunked_mac_wrapper.h"
#include <memory>
#include <string>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/str_cat.h"
#include "tink/chunked_mac.h"
#include "tink/mac/internal/chunked_mac_impl.h"
#include "tink/subtle/mac/stateful_mac.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "tink/util/test_matchers.h"
#include "proto/tink.pb.h"
namespace crypto {
namespace tink {
namespace internal {
namespace {
using ::crypto::tink::test::IsOk;
using ::crypto::tink::test::IsOkAndHolds;
using ::crypto::tink::test::StatusIs;
using ::google::crypto::tink::KeysetInfo;
using ::google::crypto::tink::KeyStatusType;
using ::google::crypto::tink::OutputPrefixType;
using ::testing::Values;
class FakeStatefulMac : public subtle::StatefulMac {
public:
explicit FakeStatefulMac(absl::string_view name) : name_(name) {}
util::Status Update(absl::string_view data) override {
absl::StrAppend(&buffer_, data);
return util::OkStatus();
}
util::StatusOr<std::string> Finalize() override {
return absl::StrCat(name_, buffer_);
}
private:
const std::string name_;
std::string buffer_ = "";
};
class FakeStatefulMacFactory : public subtle::StatefulMacFactory {
public:
explicit FakeStatefulMacFactory(absl::string_view name) : name_(name) {}
util::StatusOr<std::unique_ptr<subtle::StatefulMac>> Create() const override {
return std::unique_ptr<subtle::StatefulMac>(
absl::make_unique<FakeStatefulMac>(name_));
}
private:
std::string name_;
};
TEST(ChunkedMacWrapperTest, WrapNullptr) {
EXPECT_THAT(ChunkedMacWrapper().Wrap(nullptr).status(),
StatusIs(absl::StatusCode::kInternal));
}
TEST(ChunkedMacWrapperTest, WrapEmpty) {
std::unique_ptr<PrimitiveSet<ChunkedMac>> mac_set(
new PrimitiveSet<ChunkedMac>());
EXPECT_THAT(ChunkedMacWrapper().Wrap(std::move(mac_set)).status(),
StatusIs(absl::StatusCode::kInvalidArgument));
}
std::unique_ptr<ChunkedMac> CreateFakeChunkedMac(absl::string_view name) {
return absl::make_unique<ChunkedMacImpl>(
absl::make_unique<FakeStatefulMacFactory>(name));
}
util::Status AddPrimitiveToSet(uint32_t key_id, bool set_primary,
OutputPrefixType output_prefix_type,
std::unique_ptr<ChunkedMac> mac,
KeysetInfo& keyset_info,
PrimitiveSet<ChunkedMac>& mac_set) {
int index = keyset_info.key_info_size();
KeysetInfo::KeyInfo* key_info = keyset_info.add_key_info();
key_info->set_output_prefix_type(output_prefix_type);
key_info->set_key_id(key_id);
key_info->set_status(KeyStatusType::ENABLED);
auto entry =
mac_set.AddPrimitive(std::move(mac), keyset_info.key_info(index));
if (!entry.ok()) {
return entry.status();
}
if (set_primary) {
util::Status set_primary_status = mac_set.set_primary(*entry);
if (!set_primary_status.ok()) {
return set_primary_status;
}
}
return util::OkStatus();
}
TEST(ChunkedMacWrapperTest, ComputeMac) {
KeysetInfo keyset_info;
auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>();
// Add primitives to the primitive set.
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::TINK,
CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set),
IsOk());
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0xb1539, /*set_primary=*/false, OutputPrefixType::LEGACY,
CreateFakeChunkedMac("chunkedmac1:"), keyset_info, *mac_set),
IsOk());
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::TINK,
CreateFakeChunkedMac("chunkedmac2:"), keyset_info, *mac_set),
IsOk());
// Wrap primitive set into a ChunkedMac.
util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>> chunked_mac =
ChunkedMacWrapper().Wrap(std::move(mac_set));
ASSERT_THAT(chunked_mac.status(), IsOk());
util::StatusOr<std::unique_ptr<ChunkedMacComputation>> computation =
(*chunked_mac)->CreateComputation();
EXPECT_THAT(computation.status(), IsOk());
EXPECT_THAT((*computation)->Update("inputdata"), IsOk());
util::StatusOr<std::string> tag = (*computation)->ComputeMac();
const std::string output_prefix = std::string("\x01\x00\x6e\x12\xaf", 5);
const std::string raw_tag = "chunkedmac2:inputdata";
EXPECT_THAT(tag, IsOkAndHolds(absl::StrCat(output_prefix, raw_tag)));
}
TEST(ChunkedMacWrapperTest, VerifyMacWithUniquePrefix) {
KeysetInfo keyset_info;
auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>();
// Add primitives to primitive set.
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::TINK,
CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set),
IsOk());
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0xb1539, /*set_primary=*/false, OutputPrefixType::LEGACY,
CreateFakeChunkedMac("chunkedmac1:"), keyset_info, *mac_set),
IsOk());
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::TINK,
CreateFakeChunkedMac("chunkedmac2:"), keyset_info, *mac_set),
IsOk());
// Wrap primitive set into a ChunkedMac.
util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>>
chunked_mac = ChunkedMacWrapper().Wrap(std::move(mac_set));
ASSERT_THAT(chunked_mac.status(), IsOk());
const std::string output_prefix = std::string("\x01\x00\x6e\x12\xaf", 5);
const std::string raw_tag = "chunkedmac2:inputdata";
util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
(*chunked_mac)
->CreateVerification(absl::StrCat(output_prefix, raw_tag));
EXPECT_THAT(verification.status(), IsOk());
EXPECT_THAT((*verification)->Update("inputdata"), IsOk());
EXPECT_THAT((*verification)->VerifyMac(), IsOk());
}
TEST(ChunkedMacWrapperTest, VerifyMacWithDuplicatePrefix) {
KeysetInfo keyset_info;
auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>();
// Add primitives to primitive set.
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::LEGACY,
CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set),
IsOk());
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0x6e12af, /*set_primary=*/false, OutputPrefixType::TINK,
CreateFakeChunkedMac("chunkedmac1:"), keyset_info, *mac_set),
IsOk());
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::TINK,
CreateFakeChunkedMac("chunkedmac2:"), keyset_info, *mac_set),
IsOk());
// Wrap primitive set into a ChunkedMac.
util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>>
chunked_mac = ChunkedMacWrapper().Wrap(std::move(mac_set));
ASSERT_THAT(chunked_mac.status(), IsOk());
const std::string output_prefix = std::string("\x01\x00\x6e\x12\xaf", 5);
const std::string raw_tag = "chunkedmac1:inputdata";
util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
(*chunked_mac)
->CreateVerification(absl::StrCat(output_prefix, raw_tag));
EXPECT_THAT(verification.status(), IsOk());
EXPECT_THAT((*verification)->Update("inputdata"), IsOk());
EXPECT_THAT((*verification)->VerifyMac(), IsOk());
}
TEST(ChunkedMacWrapperTest, VerifyMacWithRawTagStartingWithKeyId) {
KeysetInfo keyset_info;
auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>();
const std::string key_id0 = std::string("\x01\x00\x12\xd6\x6f", 5);
// Add primitives to primitive set.
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::TINK,
CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set),
IsOk());
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::RAW,
CreateFakeChunkedMac(/*name=*/absl::StrCat(key_id0, ":chunkedmac1:")),
keyset_info, *mac_set),
IsOk());
// Wrap primitive set into a ChunkedMac.
util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>>
chunked_mac = ChunkedMacWrapper().Wrap(std::move(mac_set));
ASSERT_THAT(chunked_mac.status(), IsOk());
const std::string raw_tag = absl::StrCat(key_id0, ":chunkedmac1:inputdata");
util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
(*chunked_mac)->CreateVerification(raw_tag);
EXPECT_THAT(verification.status(), IsOk());
EXPECT_THAT((*verification)->Update("inputdata"), IsOk());
EXPECT_THAT((*verification)->VerifyMac(), IsOk());
}
class ChunkedMacWrapperOutputPrefixTest
: public testing::TestWithParam<OutputPrefixType> {};
INSTANTIATE_TEST_SUITE_P(
ChunkedMacWrapperOutputPrefixTestSuite, ChunkedMacWrapperOutputPrefixTest,
Values(OutputPrefixType::LEGACY, OutputPrefixType::RAW,
OutputPrefixType::CRUNCHY, OutputPrefixType::TINK));
TEST_P(ChunkedMacWrapperOutputPrefixTest, ComputeVerifyMac) {
OutputPrefixType output_prefix_type = GetParam();
KeysetInfo keyset_info;
auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>();
// Add primitives to primitive set.
ASSERT_THAT(
AddPrimitiveToSet(
/*key_id=*/0x12d66f, /*set_primary=*/true, output_prefix_type,
CreateFakeChunkedMac("chunkedmac:"), keyset_info, *mac_set),
IsOk());
// Wrap primitive set into a ChunkedMac.
util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>> chunked_mac =
ChunkedMacWrapper().Wrap(std::move(mac_set));
ASSERT_THAT(chunked_mac.status(), IsOk());
// Compute MAC via wrapper.
util::StatusOr<std::unique_ptr<ChunkedMacComputation>> mac_computation =
(*chunked_mac)->CreateComputation();
ASSERT_THAT(mac_computation.status(), IsOk());
ASSERT_THAT((*mac_computation)->Update("inputdata"), IsOk());
util::StatusOr<std::string> tag = (*mac_computation)->ComputeMac();
ASSERT_THAT(tag.status(), IsOk());
// Verify MAC via wrapper.
util::StatusOr<std::unique_ptr<ChunkedMacVerification>> mac_verification =
(*chunked_mac)->CreateVerification(*tag);
ASSERT_THAT(mac_verification.status(), IsOk());
ASSERT_THAT((*mac_verification)->Update("inputdata"), IsOk());
ASSERT_THAT((*mac_verification)->VerifyMac(), IsOk());
}
} // namespace
} // namespace internal
} // namespace tink
} // namespace crypto