blob: 3dcd35ad9c4ede1d6dbb95581ccf62a4602be234 [file] [log] [blame]
// Copyright 2024 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/keyderivation/keyset_deriver.h"
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "tink/aead/aes_gcm_key.h"
#include "tink/aead/aes_gcm_parameters.h"
#include "tink/aead/aes_gcm_proto_serialization.h"
#include "tink/config/global_registry.h"
#include "tink/insecure_secret_key_access.h"
#include "tink/internal/mutable_serialization_registry.h"
#include "tink/internal/proto_parameters_serialization.h"
#include "tink/internal/serialization.h"
#include "tink/key.h"
#include "tink/keyderivation/internal/prf_based_deriver_key_manager.h"
#include "tink/keyderivation/keyset_deriver_wrapper.h"
#include "tink/keyset_handle.h"
#include "tink/partial_key_access.h"
#include "tink/partial_key_access_token.h"
#include "tink/registry.h"
#include "tink/restricted_data.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "tink/util/test_keyset_handle.h"
#include "tink/util/test_matchers.h"
#include "tink/util/test_util.h"
#include "proto/hkdf_prf.pb.h"
#include "proto/prf_based_deriver.pb.h"
#include "proto/tink.pb.h"
namespace crypto {
namespace tink {
namespace {
using ::crypto::tink::test::IsOk;
using ::google::crypto::tink::HashType;
using ::google::crypto::tink::KeyData;
using ::google::crypto::tink::Keyset;
using ::google::crypto::tink::KeyStatusType;
using ::google::crypto::tink::KeyTemplate;
using ::testing::Eq;
using ::testing::TestWithParam;
using ::testing::ValuesIn;
// Hex values from HKDF RFC https://tools.ietf.org/html/rfc5869#appendix-A.2.
static constexpr absl::string_view kOutputKeyMaterialFromRfcVector =
"b11e398dc80327a1c8e7f78c596a4934"
"4f012eda2d4efad8a050cc4c19afa97c"
"59045a99cac7827271cb41c65e590e09"
"da3275600c2f09b8367793a9aca3db71"
"cc30c58179ec3e87c14c01d5c1f3434f";
KeyData PrfKeyFromRfcVector() {
google::crypto::tink::HkdfPrfKey prf_key;
prf_key.set_version(0);
prf_key.mutable_params()->set_hash(HashType::SHA256);
prf_key.mutable_params()->set_salt(
test::HexDecodeOrDie("606162636465666768696a6b6c6d6e6f"
"707172737475767778797a7b7c7d7e7f"
"808182838485868788898a8b8c8d8e8f"
"909192939495969798999a9b9c9d9e9f"
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"));
prf_key.set_key_value(
test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f"
"101112131415161718191a1b1c1d1e1f"
"202122232425262728292a2b2c2d2e2f"
"303132333435363738393a3b3c3d3e3f"
"404142434445464748494a4b4c4d4e4f"));
return test::AsKeyData(prf_key, KeyData::SYMMETRIC);
}
std::string SaltFromRfcVector() {
return test::HexDecodeOrDie(
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
"c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
"e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
}
std::unique_ptr<AesGcmKey> CreateAesGcmKey(int key_size,
AesGcmParameters::Variant variant,
absl::string_view secret,
absl::optional<int> id_requirement) {
AesGcmParameters params = AesGcmParameters::Builder()
.SetKeySizeInBytes(key_size)
.SetIvSizeInBytes(12)
.SetTagSizeInBytes(16)
.SetVariant(variant)
.Build()
.value();
return std::make_unique<AesGcmKey>(
AesGcmKey::Create(params,
RestrictedData(test::HexDecodeOrDie(secret),
InsecureSecretKeyAccess::Get()),
id_requirement, GetPartialKeyAccess())
.value());
}
std::vector<std::vector<std::shared_ptr<Key>>> TestVectors() {
return {
/*AesGcm KeysetHandle*/ {
{
CreateAesGcmKey(
/*key_size=*/16,
/*variant=*/AesGcmParameters::Variant::kTink,
/*secret=*/kOutputKeyMaterialFromRfcVector.substr(0, 32),
/*id_requirement=*/1010101),
},
{
CreateAesGcmKey(
/*key_size=*/32,
/*variant=*/AesGcmParameters::Variant::kCrunchy,
/*secret=*/kOutputKeyMaterialFromRfcVector.substr(0, 64),
/*id_requirement=*/2020202),
},
{
CreateAesGcmKey(
/*key_size=*/16,
/*variant=*/AesGcmParameters::Variant::kNoPrefix,
/*secret=*/kOutputKeyMaterialFromRfcVector.substr(0, 32),
/*id_requirement=*/absl::nullopt),
},
},
};
}
util::StatusOr<std::unique_ptr<KeysetHandle>> CreatePrfBasedDeriverHandle(
std::vector<std::shared_ptr<Key>> derived_keys) {
Keyset keyset;
keyset.set_primary_key_id(derived_keys[0]->GetIdRequirement().value_or(0));
for (const auto& derived_key : derived_keys) {
// Get KeyTemplate from Parameters.
util::StatusOr<std::unique_ptr<Serialization>> serialization =
internal::MutableSerializationRegistry::GlobalInstance()
.SerializeParameters<internal::ProtoParametersSerialization>(
derived_key->GetParameters());
if (!serialization.ok()) {
return serialization.status();
}
const internal::ProtoParametersSerialization* proto_serialization =
dynamic_cast<const internal::ProtoParametersSerialization*>(
serialization->get());
if (proto_serialization == nullptr) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Serialization is not ProtoParametersSerialization");
}
KeyTemplate derived_key_template = proto_serialization->GetKeyTemplate();
// Create PrfBasedDeriverKey.
google::crypto::tink::PrfBasedDeriverKey deriver_key;
deriver_key.set_version(0);
*deriver_key.mutable_prf_key() = PrfKeyFromRfcVector();
*deriver_key.mutable_params()->mutable_derived_key_template() =
derived_key_template;
// Add PrfBasedDeriverKey to Keyset.
Keyset::Key* keyset_key = keyset.add_key();
*(keyset_key->mutable_key_data()) =
test::AsKeyData(deriver_key, KeyData::SYMMETRIC);
keyset_key->set_status(KeyStatusType::ENABLED);
keyset_key->set_output_prefix_type(
derived_key_template.output_prefix_type());
keyset_key->set_key_id(derived_key->GetIdRequirement().value_or(0));
}
return TestKeysetHandle::GetKeysetHandle(keyset);
}
using KeysetDeriverTest = TestWithParam<std::vector<std::shared_ptr<Key>>>;
INSTANTIATE_TEST_SUITE_P(KeysetDeriverTests, KeysetDeriverTest,
ValuesIn(TestVectors()));
TEST_P(KeysetDeriverTest, DeriveKeyset) {
ASSERT_THAT(Registry::RegisterPrimitiveWrapper(
absl::make_unique<KeysetDeriverWrapper>()),
IsOk());
ASSERT_THAT(
Registry::RegisterKeyTypeManager(
absl::make_unique<internal::PrfBasedDeriverKeyManager>(), true),
IsOk());
ASSERT_THAT(RegisterAesGcmProtoSerialization(), IsOk());
std::vector<std::shared_ptr<Key>> derived_keys = GetParam();
util::StatusOr<std::unique_ptr<KeysetHandle>> handle =
CreatePrfBasedDeriverHandle(derived_keys);
ASSERT_THAT(handle, IsOk());
ASSERT_THAT((*handle)->size(), Eq(derived_keys.size()));
util::StatusOr<std::unique_ptr<KeysetDeriver>> deriver =
(*handle)->GetPrimitive<KeysetDeriver>(ConfigGlobalRegistry());
ASSERT_THAT(deriver, IsOk());
util::StatusOr<std::unique_ptr<KeysetHandle>> derived_handle =
(*deriver)->DeriveKeyset(SaltFromRfcVector());
ASSERT_THAT(derived_handle, IsOk());
ASSERT_THAT((*derived_handle)->size(), Eq(derived_keys.size()));
for (int i = 0; i < derived_keys.size(); i++) {
EXPECT_THAT(*(**derived_handle)[i].GetKey(),
Eq(std::ref(*derived_keys[i])));
}
}
} // namespace
} // namespace tink
} // namespace crypto