diff options
author | sschmieg <sschmieg@google.com> | 2020-04-01 10:37:59 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2020-04-01 10:38:39 -0700 |
commit | 24b654a2e06b9eec1e25006426067d9c18f43986 (patch) | |
tree | d5923faf4bff2075763bf8d0e8b6b9f234c2974f /cc/prf | |
parent | 9d84d079465428874d99378dd0231c411dab18f9 (diff) | |
download | tink-24b654a2e06b9eec1e25006426067d9c18f43986.tar.gz |
Adding KeyTypeManager for AES-CMAC PRF for the PrfSet primitive.
PiperOrigin-RevId: 304215113
Diffstat (limited to 'cc/prf')
-rw-r--r-- | cc/prf/BUILD.bazel | 45 | ||||
-rw-r--r-- | cc/prf/CMakeLists.txt | 42 | ||||
-rw-r--r-- | cc/prf/aes_cmac_prf_key_manager.h | 137 | ||||
-rw-r--r-- | cc/prf/aes_cmac_prf_key_manager_test.cc | 188 | ||||
-rw-r--r-- | cc/prf/prf_key_templates.cc | 6 | ||||
-rw-r--r-- | cc/prf/prf_key_templates_test.cc | 13 |
6 files changed, 426 insertions, 5 deletions
diff --git a/cc/prf/BUILD.bazel b/cc/prf/BUILD.bazel index 24581a49a..f70f060aa 100644 --- a/cc/prf/BUILD.bazel +++ b/cc/prf/BUILD.bazel @@ -48,6 +48,7 @@ cc_library( include_prefix = "tink/prf", visibility = ["//visibility:public"], deps = [ + ":aes_cmac_prf_key_manager", "//proto:aes_cmac_prf_cc_proto", "//proto:hkdf_prf_cc_proto", "//proto:hmac_prf_cc_proto", @@ -68,6 +69,32 @@ cc_library( ], ) +cc_library( + name = "aes_cmac_prf_key_manager", + hdrs = ["aes_cmac_prf_key_manager.h"], + include_prefix = "tink/prf", + deps = [ + "//:core/key_type_manager", + "//:key_manager", + "//proto:aes_cmac_prf_cc_proto", + "//proto:tink_cc_proto", + "//subtle", + "//subtle:common_enums", + "//subtle:random", + "//subtle:stateful_cmac_boringssl", + "//subtle/prf:prf_set_util", + "//util:constants", + "//util:errors", + "//util:protobuf_helper", + "//util:secret_data", + "//util:status", + "//util:statusor", + "//util:validation", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/strings", + ], +) + cc_test( name = "hkdf_prf_key_manager_test", srcs = ["hkdf_prf_key_manager_test.cc"], @@ -87,9 +114,12 @@ cc_test( name = "prf_key_templates_test", srcs = ["prf_key_templates_test.cc"], deps = [ + ":aes_cmac_prf_key_manager", ":hkdf_prf_key_manager", ":prf_key_templates", + "//proto:aes_cmac_prf_cc_proto", "//util:test_matchers", + "@com_google_absl//absl/memory", "@com_google_googletest//:gtest_main", ], ) @@ -105,3 +135,18 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "aes_cmac_prf_key_manager_test", + srcs = ["aes_cmac_prf_key_manager_test.cc"], + deps = [ + ":aes_cmac_prf_key_manager", + "//proto:aes_cmac_prf_cc_proto", + "//subtle:aes_cmac_boringssl", + "//util:istream_input_stream", + "//util:status", + "//util:statusor", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/cc/prf/CMakeLists.txt b/cc/prf/CMakeLists.txt index c05b54615..1ece39131 100644 --- a/cc/prf/CMakeLists.txt +++ b/cc/prf/CMakeLists.txt @@ -41,6 +41,7 @@ tink_cc_library( prf_key_templates.h prf_key_templates.cc DEPS + tink::prf::aes_cmac_prf_key_manager tink::proto::aes_cmac_prf_cc_proto tink::proto::hkdf_prf_cc_proto tink::proto::hmac_prf_cc_proto @@ -58,6 +59,30 @@ tink_cc_library( absl::strings ) +tink_cc_library( + NAME aes_cmac_prf_key_manager + SRCS aes_cmac_prf_key_manager.h + DEPS + tink::core::key_type_manager + tink::core::key_manager + tink::proto::aes_cmac_prf_cc_proto + tink::proto::tink_cc_proto + tink::subtle::common_enums + tink::subtle::random + tink::subtle::stateful_cmac_boringssl + tink::subtle::prf::prf_set_util + tink::util::constants + tink::util::errors + tink::util::enums + tink::util::protobuf_helper + tink::util::secret_data + tink::util::status + tink::util::statusor + tink::util::validation + absl::memory + absl::strings +) + tink_cc_test( NAME hkdf_prf_key_manager_test SRCS hkdf_prf_key_manager_test.cc @@ -78,9 +103,12 @@ tink_cc_test( NAME prf_key_templates_test SRCS prf_key_templates_test.cc DEPS + tink::prf::aes_cmac_prf_key_manager tink::prf::hkdf_prf_key_manager tink::prf::prf_key_templates + tink::proto::aes_cmac_prf_cc_proto tink::util::test_matchers + absl::memory gmock ) @@ -94,3 +122,17 @@ tink_cc_test( absl::strings gmock ) + +tink_cc_test( + NAME aes_cmac_prf_key_manager_test + SRCS aes_cmac_prf_key_manager_test.cc + DEPS + tink::prf::aes_cmac_prf_key_manager + tink::proto::aes_cmac_prf_cc_proto + tink::subtle::aes_cmac_boringssl + tink::util::istream_input_stream + tink::util::status + tink::util::statusor + tink::util::test_matchers + gmock +) diff --git a/cc/prf/aes_cmac_prf_key_manager.h b/cc/prf/aes_cmac_prf_key_manager.h new file mode 100644 index 000000000..6aa6bc0c8 --- /dev/null +++ b/cc/prf/aes_cmac_prf_key_manager.h @@ -0,0 +1,137 @@ +// 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. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef TINK_PRF_AES_CMAC_PRF_KEY_MANAGER_H_ +#define TINK_PRF_AES_CMAC_PRF_KEY_MANAGER_H_ + +#include <algorithm> +#include <vector> + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "tink/core/key_type_manager.h" +#include "tink/key_manager.h" +#include "tink/subtle/prf/prf_set_util.h" +#include "tink/subtle/random.h" +#include "tink/subtle/stateful_cmac_boringssl.h" +#include "tink/util/constants.h" +#include "tink/util/errors.h" +#include "tink/util/input_stream_util.h" +#include "tink/util/protobuf_helper.h" +#include "tink/util/secret_data.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "tink/util/validation.h" +#include "proto/aes_cmac_prf.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { + +class AesCmacPrfKeyManager + : public KeyTypeManager<google::crypto::tink::AesCmacPrfKey, + google::crypto::tink::AesCmacPrfKeyFormat, + List<PrfSet>> { + public: + class PrfSetFactory : public PrimitiveFactory<PrfSet> { + crypto::tink::util::StatusOr<std::unique_ptr<PrfSet>> Create( + const google::crypto::tink::AesCmacPrfKey& key) const override { + return subtle::CreatePrfSetFromPrf( + subtle::CreatePrfFromStatefulMacFactory( + absl::make_unique<subtle::StatefulCmacBoringSslFactory>( + AesCmacPrfKeyManager::MaxOutputLength(), + util::SecretDataFromStringView(key.key_value())))); + } + }; + + AesCmacPrfKeyManager() + : KeyTypeManager( + absl::make_unique<AesCmacPrfKeyManager::PrfSetFactory>()) {} + + uint32_t get_version() const override { return 0; } + + google::crypto::tink::KeyData::KeyMaterialType key_material_type() + const override { + return google::crypto::tink::KeyData::SYMMETRIC; + } + + static uint64_t MaxOutputLength() { return 16; } + const std::string& get_key_type() const override { return key_type_; } + + crypto::tink::util::Status ValidateKey( + const google::crypto::tink::AesCmacPrfKey& key) const override { + crypto::tink::util::Status status = + ValidateVersion(key.version(), get_version()); + if (!status.ok()) return status; + if (key.key_value().size() != kKeySizeInBytes) { + return crypto::tink::util::Status( + util::error::INVALID_ARGUMENT, + "Invalid AesCmacPrfKey: key_value wrong length."); + } + return util::OkStatus(); + } + + crypto::tink::util::Status ValidateKeyFormat( + const google::crypto::tink::AesCmacPrfKeyFormat& key_format) + const override { + crypto::tink::util::Status status = + ValidateVersion(key_format.version(), get_version()); + if (!status.ok()) return status; + if (key_format.key_size() != kKeySizeInBytes) { + return crypto::tink::util::Status( + crypto::tink::util::error::INVALID_ARGUMENT, + "Invalid AesCmacPrfKeyFormat: invalid key_size."); + } + return util::OkStatus(); + } + + crypto::tink::util::StatusOr<google::crypto::tink::AesCmacPrfKey> CreateKey( + const google::crypto::tink::AesCmacPrfKeyFormat& key_format) + const override { + google::crypto::tink::AesCmacPrfKey key; + key.set_version(get_version()); + key.set_key_value(subtle::Random::GetRandomBytes(key_format.key_size())); + return key; + } + + crypto::tink::util::StatusOr<google::crypto::tink::AesCmacPrfKey> DeriveKey( + const google::crypto::tink::AesCmacPrfKeyFormat& key_format, + InputStream* input_stream) const override { + auto status = ValidateKeyFormat(key_format); + if (!status.ok()) { + return status; + } + crypto::tink::util::StatusOr<std::string> randomness = + ReadBytesFromStream(key_format.key_size(), input_stream); + if (!randomness.status().ok()) { + return randomness.status(); + } + google::crypto::tink::AesCmacPrfKey key; + key.set_version(get_version()); + key.set_key_value(randomness.ValueOrDie()); + return key; + } + + private: + // Due to https://www.math.uwaterloo.ca/~ajmeneze/publications/tightness.pdf, + // we only allow key sizes of 256 bit. + const int kKeySizeInBytes = 32; + + const std::string key_type_ = absl::StrCat( + kTypeGoogleapisCom, google::crypto::tink::AesCmacPrfKey().GetTypeName()); +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_PRF_AES_CMAC_PRF_KEY_MANAGER_H_ diff --git a/cc/prf/aes_cmac_prf_key_manager_test.cc b/cc/prf/aes_cmac_prf_key_manager_test.cc new file mode 100644 index 000000000..84c9fef93 --- /dev/null +++ b/cc/prf/aes_cmac_prf_key_manager_test.cc @@ -0,0 +1,188 @@ +// 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/prf/aes_cmac_prf_key_manager.h" + +#include <sstream> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/subtle/aes_cmac_boringssl.h" +#include "tink/util/istream_input_stream.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "proto/aes_cmac_prf.pb.h" + +namespace crypto { +namespace tink { + +namespace { + +using ::crypto::tink::test::IsOk; +using ::google::crypto::tink::AesCmacPrfKey; +using ::google::crypto::tink::AesCmacPrfKeyFormat; +using ::testing::Eq; +using ::testing::Not; +using ::testing::SizeIs; +using ::testing::StrEq; + +std::unique_ptr<InputStream> GetInputStreamForString(const std::string& input) { + return absl::make_unique<util::IstreamInputStream>( + absl::make_unique<std::stringstream>(input)); +} + +AesCmacPrfKeyFormat ValidKeyFormat() { + AesCmacPrfKeyFormat format; + format.set_key_size(32); + return format; +} + +TEST(AesCmacPrfKeyManagerTest, Basics) { + EXPECT_THAT(AesCmacPrfKeyManager().get_version(), Eq(0)); + EXPECT_THAT(AesCmacPrfKeyManager().get_key_type(), + Eq("type.googleapis.com/google.crypto.tink.AesCmacPrfKey")); + EXPECT_THAT(AesCmacPrfKeyManager().key_material_type(), + Eq(google::crypto::tink::KeyData::SYMMETRIC)); +} + +TEST(AesCmacPrfKeyManagerTest, ValidateEmptyKey) { + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKey(AesCmacPrfKey()), Not(IsOk())); +} + +TEST(AesCmacPrfKeyManagerTest, ValidateEmptyKeyFormat) { + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKeyFormat(AesCmacPrfKeyFormat()), + Not(IsOk())); +} + +TEST(AesCmacPrfKeyManagerTest, ValidateSimpleKeyFormat) { + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKeyFormat(ValidKeyFormat()), + IsOk()); +} + +TEST(AesCmacPrfKeyManagerTest, ValidateKeyFormatKeySizes) { + AesCmacPrfKeyFormat format = ValidKeyFormat(); + + format.set_key_size(0); + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKeyFormat(format), Not(IsOk())); + + format.set_key_size(1); + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKeyFormat(format), Not(IsOk())); + + format.set_key_size(15); + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKeyFormat(format), Not(IsOk())); + + format.set_key_size(16); + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKeyFormat(format), Not(IsOk())); + + format.set_key_size(17); + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKeyFormat(format), Not(IsOk())); + + format.set_key_size(31); + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKeyFormat(format), Not(IsOk())); + + format.set_key_size(32); + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKeyFormat(format), IsOk()); + + format.set_key_size(33); + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKeyFormat(format), Not(IsOk())); +} + +TEST(AesCmacPrfKeyManagerTest, CreateKey) { + AesCmacPrfKeyFormat format = ValidKeyFormat(); + ASSERT_THAT(AesCmacPrfKeyManager().CreateKey(format).status(), IsOk()); + AesCmacPrfKey key = AesCmacPrfKeyManager().CreateKey(format).ValueOrDie(); + EXPECT_THAT(key.version(), Eq(0)); + EXPECT_THAT(key.key_value(), SizeIs(format.key_size())); +} + +TEST(AesCmacPrfKeyManagerTest, ValidateKey) { + AesCmacPrfKeyFormat format = ValidKeyFormat(); + AesCmacPrfKey key = AesCmacPrfKeyManager().CreateKey(format).ValueOrDie(); + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKey(key), IsOk()); +} + +TEST(AesCmacPrfKeyManagerTest, ValidateKeyInvalidVersion) { + AesCmacPrfKeyFormat format = ValidKeyFormat(); + AesCmacPrfKey key = AesCmacPrfKeyManager().CreateKey(format).ValueOrDie(); + key.set_version(1); + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKey(key), Not(IsOk())); +} + +TEST(AesCmacPrfKeyManagerTest, ValidateKeyShortKey) { + AesCmacPrfKeyFormat format = ValidKeyFormat(); + AesCmacPrfKey key = AesCmacPrfKeyManager().CreateKey(format).ValueOrDie(); + key.set_key_value("0123456789abcdef"); + EXPECT_THAT(AesCmacPrfKeyManager().ValidateKey(key), Not(IsOk())); +} + +TEST(AesCmacPrfKeyManagerTest, GetPrimitive) { + AesCmacPrfKeyFormat format = ValidKeyFormat(); + AesCmacPrfKey key = AesCmacPrfKeyManager().CreateKey(format).ValueOrDie(); + auto manager_prf_or = AesCmacPrfKeyManager().GetPrimitive<PrfSet>(key); + ASSERT_THAT(manager_prf_or.status(), IsOk()); + auto prf_value_or = + manager_prf_or.ValueOrDie()->ComputePrimary("some plaintext", 16); + ASSERT_THAT(prf_value_or.status(), IsOk()); + + auto direct_prf_or = subtle::AesCmacBoringSsl::New( + util::SecretDataFromStringView(key.key_value()), 16); + ASSERT_THAT(direct_prf_or.status(), IsOk()); + auto direct_prf_value_or = + direct_prf_or.ValueOrDie()->ComputeMac("some plaintext"); + ASSERT_THAT(direct_prf_value_or.status(), IsOk()); + EXPECT_THAT(direct_prf_value_or.ValueOrDie(), + StrEq(prf_value_or.ValueOrDie())); +} + +TEST(AesCmacPrfKeyManagerTest, DeriveKeyValid) { + std::string bytes = "0123456789abcdef0123456789abcdef"; + auto inputstream = GetInputStreamForString(bytes); + auto key_or = + AesCmacPrfKeyManager().DeriveKey(ValidKeyFormat(), inputstream.get()); + ASSERT_THAT(key_or.status(), IsOk()); + AesCmacPrfKey key = key_or.ValueOrDie(); + EXPECT_THAT(key.version(), Eq(AesCmacPrfKeyManager().get_version())); + EXPECT_THAT(key.key_value(), Eq(bytes)); +} + +TEST(AesCmacPrfKeyManagerTest, DeriveKeyNotEnoughRandomness) { + std::string bytes = "0123456789abcdef"; + auto inputstream = GetInputStreamForString(bytes); + auto key_or = + AesCmacPrfKeyManager().DeriveKey(ValidKeyFormat(), inputstream.get()); + EXPECT_THAT(key_or.status(), Not(IsOk())); +} + +TEST(AesCmacPrfKeyManagerTest, DeriveKeyInvalidFormat) { + std::string bytes = "0123456789abcdef"; + auto inputstream = GetInputStreamForString(bytes); + auto format = ValidKeyFormat(); + format.set_key_size(12); + auto key_or = AesCmacPrfKeyManager().DeriveKey(format, inputstream.get()); + EXPECT_THAT(key_or.status(), Not(IsOk())); +} + +TEST(AesCmacPrfKeyManagerTest, DeriveKeyInvalidVersion) { + auto format = ValidKeyFormat(); + format.set_version(1); + std::string bytes = "0123456789abcdef0123456789abcdef"; + auto inputstream = GetInputStreamForString(bytes); + auto key_or = + AesCmacPrfKeyManager().DeriveKey(format, inputstream.get()); + EXPECT_THAT(key_or.status(), Not(IsOk())); +} + +} // namespace +} // namespace tink +} // namespace crypto diff --git a/cc/prf/prf_key_templates.cc b/cc/prf/prf_key_templates.cc index 14e9c8fae..7cba04f91 100644 --- a/cc/prf/prf_key_templates.cc +++ b/cc/prf/prf_key_templates.cc @@ -14,6 +14,7 @@ #include "tink/prf/prf_key_templates.h" #include "absl/memory/memory.h" +#include "tink/prf/aes_cmac_prf_key_manager.h" #include "proto/aes_cmac_prf.pb.h" #include "proto/hkdf_prf.pb.h" #include "proto/hmac_prf.pb.h" @@ -56,11 +57,12 @@ std::unique_ptr<google::crypto::tink::KeyTemplate> NewHmacTemplate( std::unique_ptr<google::crypto::tink::KeyTemplate> NewAesCmacTemplate() { auto key_template = absl::make_unique<google::crypto::tink::KeyTemplate>(); - key_template->set_type_url( - "type.googleapis.com/google.crypto.tink.AesCmacPrfKey"); + auto aes_cmac_prf_key_manager = absl::make_unique<AesCmacPrfKeyManager>(); + key_template->set_type_url(aes_cmac_prf_key_manager->get_key_type()); key_template->set_output_prefix_type( google::crypto::tink::OutputPrefixType::RAW); AesCmacPrfKeyFormat key_format; + key_format.set_version(aes_cmac_prf_key_manager->get_version()); key_format.set_key_size(32); key_format.SerializeToString(key_template->mutable_value()); return key_template; diff --git a/cc/prf/prf_key_templates_test.cc b/cc/prf/prf_key_templates_test.cc index cc52cccfb..7795b06bb 100644 --- a/cc/prf/prf_key_templates_test.cc +++ b/cc/prf/prf_key_templates_test.cc @@ -16,8 +16,11 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "tink/prf/aes_cmac_prf_key_manager.h" #include "tink/prf/hkdf_prf_key_manager.h" #include "tink/util/test_matchers.h" +#include "proto/aes_cmac_prf.pb.h" namespace crypto { namespace tink { @@ -79,7 +82,12 @@ TEST(HmacPrfTest, MultipleCallsSameReference) { TEST(CmacPrfTest, Basics) { EXPECT_THAT(PrfKeyTemplates::AesCmac().type_url(), Eq("type.googleapis.com/google.crypto.tink.AesCmacPrfKey")); - // TODO(sschmieg): Add key type manager test + auto manager = absl::make_unique<AesCmacPrfKeyManager>(); + EXPECT_THAT(PrfKeyTemplates::AesCmac().type_url(), + Eq(manager->get_key_type())); + google::crypto::tink::AesCmacPrfKeyFormat format; + ASSERT_TRUE(format.ParseFromString(PrfKeyTemplates::AesCmac().value())); + EXPECT_THAT(manager->ValidateKeyFormat(format), IsOk()); } TEST(CmacPrfTest, OutputPrefixType) { @@ -88,8 +96,7 @@ TEST(CmacPrfTest, OutputPrefixType) { } TEST(CmacPrfTest, MultipleCallsSameReference) { - EXPECT_THAT(PrfKeyTemplates::AesCmac(), - Ref(PrfKeyTemplates::AesCmac())); + EXPECT_THAT(PrfKeyTemplates::AesCmac(), Ref(PrfKeyTemplates::AesCmac())); } } // namespace |