From 732dca60a7cbb6b66b4173e93a25e376c33f06f9 Mon Sep 17 00:00:00 2001
From: tholenst <tholenst@google.com>
Date: Fri, 9 Aug 2019 00:37:21 -0700
Subject: [PATCH] Migrate the AesCmacKeyManager to a KeyTypeManager.

PiperOrigin-RevId: 262511503
---
 cc/mac/BUILD.bazel                  |   6 +-
 cc/mac/CMakeLists.txt               |   8 +-
 cc/mac/aes_cmac_key_manager.cc      | 134 -----------
 cc/mac/aes_cmac_key_manager.h       |  99 ++++++--
 cc/mac/aes_cmac_key_manager_test.cc | 354 ++++++++++++----------------
 cc/mac/mac_config.cc                |   2 +-
 cc/mac/mac_key_templates_test.cc    |  58 +++--
 7 files changed, 270 insertions(+), 391 deletions(-)
 delete mode 100644 cc/mac/aes_cmac_key_manager.cc

diff --git a/cc/mac/BUILD.bazel b/cc/mac/BUILD.bazel
index 7a10bff50..e35632a0b 100644
--- a/cc/mac/BUILD.bazel
+++ b/cc/mac/BUILD.bazel
@@ -75,16 +75,16 @@ cc_library(
 
 cc_library(
     name = "aes_cmac_key_manager",
-    srcs = ["aes_cmac_key_manager.cc"],
     hdrs = ["aes_cmac_key_manager.h"],
     include_prefix = "tink",
     strip_include_prefix = "/cc",
     deps = [
         "//cc:key_manager",
-        "//cc:key_manager_base",
+        "//cc:core/key_type_manager",
         "//cc:mac",
         "//cc/subtle:aes_cmac_boringssl",
         "//cc/subtle:random",
+        "//cc/util:constants",
         "//cc/util:enums",
         "//cc/util:errors",
         "//cc/util:protobuf_helper",
@@ -194,6 +194,7 @@ cc_test(
         ":aes_cmac_key_manager",
         ":hmac_key_manager",
         ":mac_key_templates",
+        "//cc/util:test_matchers",
         "//proto:aes_cmac_cc_proto",
         "//proto:common_cc_proto",
         "//proto:hmac_cc_proto",
@@ -212,6 +213,7 @@ cc_test(
         "//cc:mac",
         "//cc/util:status",
         "//cc/util:statusor",
+        "//cc/util:test_matchers",
         "//proto:aes_cmac_cc_proto",
         "//proto:aes_ctr_cc_proto",
         "//proto:aes_ctr_hmac_aead_cc_proto",
diff --git a/cc/mac/CMakeLists.txt b/cc/mac/CMakeLists.txt
index d8e4d0929..27af195f5 100644
--- a/cc/mac/CMakeLists.txt
+++ b/cc/mac/CMakeLists.txt
@@ -64,14 +64,14 @@ tink_cc_library(
 tink_cc_library(
   NAME aes_cmac_key_manager
   SRCS
-    aes_cmac_key_manager.cc
     aes_cmac_key_manager.h
   DEPS
     tink::core::key_manager
-    tink::core::key_manager_base
+    tink::core::key_type_manager
     tink::core::mac
     tink::subtle::aes_cmac_boringssl
     tink::subtle::random
+    tink::util::constants
     tink::util::enums
     tink::util::errors
     tink::util::protobuf_helper
@@ -164,10 +164,12 @@ tink_cc_test(
     tink::mac::aes_cmac_key_manager
     tink::mac::hmac_key_manager
     tink::mac::mac_key_templates
+    tink::util::test_matchers
     tink::proto::aes_cmac_cc_proto
     tink::proto::common_cc_proto
     tink::proto::hmac_cc_proto
     tink::proto::tink_cc_proto
+    gmock
 )
 
 tink_cc_test(
@@ -176,6 +178,7 @@ tink_cc_test(
   DEPS
     tink::mac::aes_cmac_key_manager
     tink::core::mac
+    tink::util::test_matchers
     tink::util::status
     tink::util::statusor
     tink::proto::aes_ctr_cc_proto
@@ -183,6 +186,7 @@ tink_cc_test(
     tink::proto::common_cc_proto
     tink::proto::aes_cmac_cc_proto
     tink::proto::tink_cc_proto
+    gmock
 )
 
 tink_cc_test(
diff --git a/cc/mac/aes_cmac_key_manager.cc b/cc/mac/aes_cmac_key_manager.cc
deleted file mode 100644
index 9e3e81d16..000000000
--- a/cc/mac/aes_cmac_key_manager.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2017 Google Inc.
-//
-// 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/aes_cmac_key_manager.h"
-
-#include <map>
-
-#include "absl/strings/string_view.h"
-#include "tink/key_manager.h"
-#include "tink/mac.h"
-#include "tink/subtle/aes_cmac_boringssl.h"
-#include "tink/subtle/random.h"
-#include "tink/util/enums.h"
-#include "tink/util/errors.h"
-#include "tink/util/protobuf_helper.h"
-#include "tink/util/status.h"
-#include "tink/util/statusor.h"
-#include "tink/util/validation.h"
-#include "proto/aes_cmac.pb.h"
-#include "proto/tink.pb.h"
-
-namespace crypto {
-namespace tink {
-
-using crypto::tink::util::Enums;
-using crypto::tink::util::Status;
-using crypto::tink::util::StatusOr;
-using google::crypto::tink::AesCmacKey;
-using google::crypto::tink::AesCmacKeyFormat;
-using google::crypto::tink::AesCmacParams;
-using google::crypto::tink::KeyData;
-
-constexpr uint32_t AesCmacKeyManager::kVersion;
-// Due to https://www.math.uwaterloo.ca/~ajmeneze/publications/tightness.pdf, we
-// only allow key sizes of 256 bit.
-constexpr int kKeySizeInBytes = 32;
-constexpr int kMaxTagSizeInBytes = 16;
-constexpr int kMinTagSizeInBytes = 10;
-
-class AesCmacKeyFactory : public KeyFactoryBase<AesCmacKey, AesCmacKeyFormat> {
- public:
-  AesCmacKeyFactory() {}
-
-  KeyData::KeyMaterialType key_material_type() const override {
-    return KeyData::SYMMETRIC;
-  }
-
- protected:
-  StatusOr<std::unique_ptr<AesCmacKey>> NewKeyFromFormat(
-      const AesCmacKeyFormat& cmac_key_format) const override;
-};
-
-StatusOr<std::unique_ptr<AesCmacKey>> AesCmacKeyFactory::NewKeyFromFormat(
-    const AesCmacKeyFormat& cmac_key_format) const {
-  Status status = AesCmacKeyManager::Validate(cmac_key_format);
-  if (!status.ok()) return status;
-  auto cmac_key = absl::make_unique<AesCmacKey>();
-  cmac_key->set_version(AesCmacKeyManager::kVersion);
-  cmac_key->set_key_value(
-      subtle::Random::GetRandomBytes(cmac_key_format.key_size()));
-  *cmac_key->mutable_params() = cmac_key_format.params();
-  return absl::implicit_cast<StatusOr<std::unique_ptr<AesCmacKey>>>(
-      std::move(cmac_key));
-}
-
-AesCmacKeyManager::AesCmacKeyManager()
-    : key_factory_(new AesCmacKeyFactory()) {}
-
-uint32_t AesCmacKeyManager::get_version() const { return kVersion; }
-
-const KeyFactory& AesCmacKeyManager::get_key_factory() const {
-  return *key_factory_;
-}
-
-StatusOr<std::unique_ptr<Mac>> AesCmacKeyManager::GetPrimitiveFromKey(
-    const AesCmacKey& cmac_key) const {
-  Status status = Validate(cmac_key);
-  if (!status.ok()) return status;
-  auto cmac_result = subtle::AesCmacBoringSsl::New(
-      cmac_key.key_value(), cmac_key.params().tag_size());
-  if (!cmac_result.ok()) return cmac_result.status();
-  return std::move(cmac_result.ValueOrDie());
-}
-
-// static
-Status AesCmacKeyManager::Validate(const AesCmacParams& params) {
-  if (params.tag_size() < kMinTagSizeInBytes) {
-    return ToStatusF(util::error::INVALID_ARGUMENT,
-                     "Invalid AesCmacParams: tag_size %d is too small.",
-                     params.tag_size());
-  }
-  if (params.tag_size() > kMaxTagSizeInBytes) {
-    return ToStatusF(util::error::INVALID_ARGUMENT,
-                     "Invalid AesCmacParams: tag_size %d is too big.",
-                     params.tag_size());
-  }
-  return Status::OK;
-}
-
-// static
-Status AesCmacKeyManager::Validate(const AesCmacKey& key) {
-  Status status = ValidateVersion(key.version(), kVersion);
-  if (!status.ok()) return status;
-  if (key.key_value().size() != kKeySizeInBytes) {
-    return ToStatusF(util::error::INVALID_ARGUMENT,
-                     "Invalid AesCmacKey: key_value wrong length.");
-  }
-  return Validate(key.params());
-}
-
-// static
-Status AesCmacKeyManager::Validate(const AesCmacKeyFormat& key_format) {
-  if (key_format.key_size() != kKeySizeInBytes) {
-    return ToStatusF(util::error::INVALID_ARGUMENT,
-                     "Invalid AesCmacKeyFormat: invalid key_size.");
-  }
-  return Validate(key_format.params());
-}
-
-}  // namespace tink
-}  // namespace crypto
diff --git a/cc/mac/aes_cmac_key_manager.h b/cc/mac/aes_cmac_key_manager.h
index efac6a329..3d1b81b3e 100644
--- a/cc/mac/aes_cmac_key_manager.h
+++ b/cc/mac/aes_cmac_key_manager.h
@@ -1,5 +1,3 @@
-// Copyright 2017 Google Inc.
-//
 // 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
@@ -20,13 +18,17 @@
 #include <vector>
 
 #include "absl/strings/string_view.h"
-#include "tink/core/key_manager_base.h"
+#include "tink/core/key_type_manager.h"
 #include "tink/key_manager.h"
 #include "tink/mac.h"
+#include "tink/subtle/aes_cmac_boringssl.h"
+#include "tink/subtle/random.h"
+#include "tink/util/constants.h"
 #include "tink/util/errors.h"
 #include "tink/util/protobuf_helper.h"
 #include "tink/util/status.h"
 #include "tink/util/statusor.h"
+#include "tink/util/validation.h"
 #include "proto/aes_cmac.pb.h"
 #include "proto/tink.pb.h"
 
@@ -34,36 +36,87 @@ namespace crypto {
 namespace tink {
 
 class AesCmacKeyManager
-    : public KeyManagerBase<Mac, google::crypto::tink::AesCmacKey> {
+    : public KeyTypeManager<google::crypto::tink::AesCmacKey,
+                            google::crypto::tink::AesCmacKeyFormat,
+                            List<Mac>> {
  public:
-  static constexpr uint32_t kVersion = 0;
+  class MacFactory : public PrimitiveFactory<Mac> {
+    crypto::tink::util::StatusOr<std::unique_ptr<Mac>> Create(
+        const google::crypto::tink::AesCmacKey& key) const override {
+      return subtle::AesCmacBoringSsl::New(key.key_value(),
+                                           key.params().tag_size());
+    }
+  };
+
+  AesCmacKeyManager()
+      : KeyTypeManager(absl::make_unique<AesCmacKeyManager::MacFactory>()) {}
+
+  uint32_t get_version() const override { return 0; }
 
-  AesCmacKeyManager();
+  google::crypto::tink::KeyData::KeyMaterialType key_material_type()
+      const override {
+    return google::crypto::tink::KeyData::SYMMETRIC;
+  }
 
-  // Returns the version of this key manager.
-  uint32_t get_version() const override;
+  const std::string& get_key_type() const override { return key_type_; }
 
-  // Returns a factory that generates keys of the key type
-  // handled by this manager.
-  const KeyFactory& get_key_factory() const override;
+  crypto::tink::util::Status ValidateKey(
+      const google::crypto::tink::AesCmacKey& 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 AesCmacKey: key_value wrong length.");
+    }
+    return ValidateParams(key.params());
+  }
 
-  ~AesCmacKeyManager() override {}
+  crypto::tink::util::Status ValidateKeyFormat(
+      const google::crypto::tink::AesCmacKeyFormat& key_format) const override {
+    if (key_format.key_size() != kKeySizeInBytes) {
+      return crypto::tink::util::Status(
+          crypto::tink::util::error::INVALID_ARGUMENT,
+          "Invalid AesCmacKeyFormat: invalid key_size.");
+    }
+    return ValidateParams(key_format.params());
+  }
 
- protected:
-  crypto::tink::util::StatusOr<std::unique_ptr<Mac>> GetPrimitiveFromKey(
-      const google::crypto::tink::AesCmacKey& cmac_key) const override;
+  crypto::tink::util::StatusOr<google::crypto::tink::AesCmacKey> CreateKey(
+      const google::crypto::tink::AesCmacKeyFormat& key_format) const override {
+    google::crypto::tink::AesCmacKey key;
+    key.set_version(get_version());
+    key.set_key_value(
+        subtle::Random::GetRandomBytes(key_format.key_size()));
+    *key.mutable_params() = key_format.params();
+    return key;
+  }
 
  private:
-  friend class AesCmacKeyFactory;
+  crypto::tink::util::Status ValidateParams(
+      const google::crypto::tink::AesCmacParams& params) const {
+    if (params.tag_size() < kMinTagSizeInBytes) {
+      return util::Status(util::error::INVALID_ARGUMENT,
+                          absl::StrCat("Invalid AesCmacParams: tag_size ",
+                                       params.tag_size(), " is too small."));
+    }
+    if (params.tag_size() > kMaxTagSizeInBytes) {
+      return util::Status(util::error::INVALID_ARGUMENT,
+                          absl::StrCat("Invalid AesCmacParams: tag_size ",
+                                       params.tag_size(), " is too big."));
+    }
+    return util::OkStatus();
+  }
 
-  std::unique_ptr<KeyFactory> key_factory_;
+  // Due to https://www.math.uwaterloo.ca/~ajmeneze/publications/tightness.pdf,
+  // we only allow key sizes of 256 bit.
+  const int kKeySizeInBytes = 32;
+  const int kMaxTagSizeInBytes = 16;
+  const int kMinTagSizeInBytes = 10;
 
-  static crypto::tink::util::Status Validate(
-      const google::crypto::tink::AesCmacParams& params);
-  static crypto::tink::util::Status Validate(
-      const google::crypto::tink::AesCmacKey& key);
-  static crypto::tink::util::Status Validate(
-      const google::crypto::tink::AesCmacKeyFormat& key_format);
+  const std::string key_type_ = absl::StrCat(
+      kTypeGoogleapisCom, google::crypto::tink::AesCmacKey().GetTypeName());
 };
 
 }  // namespace tink
diff --git a/cc/mac/aes_cmac_key_manager_test.cc b/cc/mac/aes_cmac_key_manager_test.cc
index 75135dc19..813684125 100644
--- a/cc/mac/aes_cmac_key_manager_test.cc
+++ b/cc/mac/aes_cmac_key_manager_test.cc
@@ -1,5 +1,3 @@
-// Copyright 2017 Google Inc.
-//
 // 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
@@ -13,234 +11,178 @@
 // limitations under the License.
 //
 ////////////////////////////////////////////////////////////////////////////////
-
 #include "tink/mac/aes_cmac_key_manager.h"
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "tink/mac.h"
 #include "tink/util/status.h"
 #include "tink/util/statusor.h"
+#include "tink/util/test_matchers.h"
 #include "proto/aes_cmac.pb.h"
-#include "proto/aes_ctr.pb.h"
-#include "proto/common.pb.h"
-#include "proto/tink.pb.h"
 
 namespace crypto {
 namespace tink {
 
-using google::crypto::tink::AesCmacKey;
-using google::crypto::tink::AesCmacKeyFormat;
-using google::crypto::tink::AesCtrKey;
-using google::crypto::tink::AesCtrKeyFormat;
-using google::crypto::tink::HashType;
-using google::crypto::tink::KeyData;
-
 namespace {
 
-class AesCmacKeyManagerTest : public ::testing::Test {
- protected:
-  std::string key_type_prefix_ = "type.googleapis.com/";
-  std::string cmac_key_type_ = "type.googleapis.com/google.crypto.tink.AesCmacKey";
-};
+using ::crypto::tink::test::IsOk;
+using ::google::crypto::tink::AesCmacKey;
+using ::google::crypto::tink::AesCmacKeyFormat;
+using ::google::crypto::tink::AesCmacParams;
+using ::testing::Eq;
+using ::testing::Not;
+using ::testing::SizeIs;
+
+TEST(AesCmacKeyManagerTest, Basics) {
+  EXPECT_THAT(AesCmacKeyManager().get_version(), Eq(0));
+  EXPECT_THAT(AesCmacKeyManager().get_key_type(),
+              Eq("type.googleapis.com/google.crypto.tink.AesCmacKey"));
+  EXPECT_THAT(AesCmacKeyManager().key_material_type(),
+              Eq(google::crypto::tink::KeyData::SYMMETRIC));
+}
 
-TEST_F(AesCmacKeyManagerTest, testBasic) {
-  AesCmacKeyManager key_manager;
+TEST(AesCmacKeyManagerTest, ValidateEmptyKey) {
+  EXPECT_THAT(AesCmacKeyManager().ValidateKey(AesCmacKey()), Not(IsOk()));
+}
 
-  EXPECT_EQ(0, key_manager.get_version());
-  EXPECT_EQ("type.googleapis.com/google.crypto.tink.AesCmacKey",
-            key_manager.get_key_type());
-  EXPECT_TRUE(key_manager.DoesSupport(key_manager.get_key_type()));
+AesCmacParams ValidParams() {
+  AesCmacParams params;
+  params.set_tag_size(16);
+  return params;
 }
 
-TEST_F(AesCmacKeyManagerTest, testKeyDataErrors) {
-  AesCmacKeyManager key_manager;
-
-  {  // Bad key type.
-    KeyData key_data;
-    std::string bad_key_type =
-        "type.googleapis.com/google.crypto.tink.SomeOtherKey";
-    key_data.set_type_url(bad_key_type);
-    auto result = key_manager.GetPrimitive(key_data);
-    EXPECT_FALSE(result.ok());
-    EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "not supported",
-                        result.status().error_message());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, bad_key_type,
-                        result.status().error_message());
-  }
-
-  {  // Bad key value.
-    KeyData key_data;
-    key_data.set_type_url(cmac_key_type_);
-    key_data.set_value("some bad serialized proto");
-    auto result = key_manager.GetPrimitive(key_data);
-    EXPECT_FALSE(result.ok());
-    EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "not parse",
-                        result.status().error_message());
-  }
-
-  {  // Bad version.
-    KeyData key_data;
-    AesCmacKey key;
-    key.set_version(1);
-    key_data.set_type_url(cmac_key_type_);
-    key_data.set_value(key.SerializeAsString());
-    auto result = key_manager.GetPrimitive(key_data);
-    EXPECT_FALSE(result.ok());
-    EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "version",
-                        result.status().error_message());
-  }
+AesCmacKeyFormat ValidKeyFormat() {
+  AesCmacKeyFormat format;
+  *format.mutable_params() = ValidParams();
+  format.set_key_size(32);
+  return format;
 }
 
-TEST_F(AesCmacKeyManagerTest, testKeyMessageErrors) {
-  AesCmacKeyManager key_manager;
-
-  {  // Bad protobuffer.
-    AesCtrKey key_message;
-    auto result = key_manager.GetPrimitive(key_message);
-    EXPECT_FALSE(result.ok());
-    EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "AesCtrKey",
-                        result.status().error_message());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "not supported",
-                        result.status().error_message());
-  }
+TEST(AesCmacKeyManagerTest, ValidateEmptyKeyFormat) {
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(AesCmacKeyFormat()),
+              Not(IsOk()));
 }
 
-TEST_F(AesCmacKeyManagerTest, testPrimitives) {
-  AesCmacKeyManager key_manager;
-  AesCmacKey key;
-
-  key.set_version(0);
-  key.mutable_params()->set_tag_size(16);
-  key.set_key_value("some key of sufficient length...");
-
-  {  // Using key message only.
-    auto result = key_manager.GetPrimitive(key);
-    EXPECT_TRUE(result.ok()) << result.status();
-    auto cmac = std::move(result.ValueOrDie());
-    auto cmac_result = cmac->ComputeMac("some data");
-    EXPECT_TRUE(cmac_result.ok());
-  }
-
-  {  // Using KeyData proto.
-    KeyData key_data;
-    key_data.set_type_url(cmac_key_type_);
-    key_data.set_value(key.SerializeAsString());
-    auto result = key_manager.GetPrimitive(key);
-    EXPECT_TRUE(result.ok()) << result.status();
-    auto cmac = std::move(result.ValueOrDie());
-    auto cmac_result = cmac->ComputeMac("some data");
-    EXPECT_TRUE(cmac_result.ok());
-  }
+TEST(AesCmacKeyManagerTest, ValidateSimpleKeyFormat) {
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(ValidKeyFormat()), IsOk());
 }
 
-TEST_F(AesCmacKeyManagerTest, testNewKeyErrors) {
-  AesCmacKeyManager key_manager;
-  const KeyFactory& key_factory = key_manager.get_key_factory();
-
-  {  // Bad key format.
-    AesCtrKeyFormat key_format;
-    auto result = key_factory.NewKey(key_format);
-    EXPECT_FALSE(result.ok());
-    EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "not supported",
-                        result.status().error_message());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "AesCtrKeyFormat",
-                        result.status().error_message());
-  }
-
-  {  // Bad serialized key format.
-    auto result = key_factory.NewKey("some bad serialized proto");
-    EXPECT_FALSE(result.ok());
-    EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "not parse",
-                        result.status().error_message());
-  }
-
-  {  // Bad AesCmacKeyFormat: small key_size.
-    AesCmacKeyFormat key_format;
-    key_format.set_key_size(8);
-    key_format.mutable_params()->set_tag_size(16);
-    auto result = key_factory.NewKey(key_format);
-    EXPECT_FALSE(result.ok());
-    EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "invalid key_size",
-                        result.status().error_message());
-  }
-
-  {  // Bad AesCmacKeyFormat: BlockCipher not supported.
-    AesCmacKeyFormat key_format;
-    key_format.set_key_size(32);
-    key_format.mutable_params()->set_tag_size(17);
-    auto result = key_factory.NewKey(key_format);
-    EXPECT_FALSE(result.ok());
-    EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "tag_size",
-                        result.status().error_message());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "too big",
-                        result.status().error_message());
-  }
-
-  {  // Bad AesCmacKeyFormat: BlockCipher not supported.
-    AesCmacKeyFormat key_format;
-    key_format.set_key_size(32);
-    key_format.mutable_params()->set_tag_size(9);
-    auto result = key_factory.NewKey(key_format);
-    EXPECT_FALSE(result.ok());
-    EXPECT_EQ(util::error::INVALID_ARGUMENT, result.status().error_code());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "tag_size",
-                        result.status().error_message());
-    EXPECT_PRED_FORMAT2(testing::IsSubstring, "too small",
-                        result.status().error_message());
-  }
+TEST(AesCmacKeyManagerTest, ValidateKeyFormatKeySizes) {
+  AesCmacKeyFormat format = ValidKeyFormat();
+
+  format.set_key_size(0);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), Not(IsOk()));
+
+  format.set_key_size(1);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), Not(IsOk()));
+
+  format.set_key_size(15);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), Not(IsOk()));
+
+  format.set_key_size(16);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), Not(IsOk()));
+
+  format.set_key_size(17);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), Not(IsOk()));
+
+  format.set_key_size(31);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), Not(IsOk()));
+
+  format.set_key_size(32);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), IsOk());
+
+  format.set_key_size(33);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), Not(IsOk()));
+}
+
+TEST(AesCmacKeyManagerTest, ValidateKeyFormatTagSizes) {
+  AesCmacKeyFormat format = ValidKeyFormat();
+
+  format.mutable_params()->set_tag_size(0);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), Not(IsOk()));
+
+  format.mutable_params()->set_tag_size(9);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), Not(IsOk()));
+
+  format.mutable_params()->set_tag_size(10);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), IsOk());
+
+  format.mutable_params()->set_tag_size(11);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), IsOk());
+
+  format.mutable_params()->set_tag_size(12);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), IsOk());
+
+  format.mutable_params()->set_tag_size(15);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), IsOk());
+
+  format.mutable_params()->set_tag_size(16);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), IsOk());
+
+  format.mutable_params()->set_tag_size(17);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), Not(IsOk()));
+
+  format.mutable_params()->set_tag_size(32);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(format), Not(IsOk()));
+}
+
+TEST(AesCmacKeyManagerTest, CreateKey) {
+  AesCmacKeyFormat format = ValidKeyFormat();
+  ASSERT_THAT(AesCmacKeyManager().CreateKey(format).status(), IsOk());
+  AesCmacKey key = AesCmacKeyManager().CreateKey(format).ValueOrDie();
+  EXPECT_THAT(key.version(), Eq(0));
+  EXPECT_THAT(key.key_value(), SizeIs(format.key_size()));
+  EXPECT_THAT(key.params().tag_size(), Eq(format.params().tag_size()));
+}
+
+TEST(AesCmacKeyManagerTest, ValidateKey) {
+  AesCmacKeyFormat format = ValidKeyFormat();
+  AesCmacKey key = AesCmacKeyManager().CreateKey(format).ValueOrDie();
+  EXPECT_THAT(AesCmacKeyManager().ValidateKey(key), IsOk());
+}
+
+TEST(AesCmacKeyManagerTest, ValidateKeyInvalidVersion) {
+  AesCmacKeyFormat format = ValidKeyFormat();
+  AesCmacKey key = AesCmacKeyManager().CreateKey(format).ValueOrDie();
+  key.set_version(1);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKey(key), Not(IsOk()));
+}
+
+TEST(AesCmacKeyManagerTest, ValidateKeyShortKey) {
+  AesCmacKeyFormat format = ValidKeyFormat();
+  AesCmacKey key = AesCmacKeyManager().CreateKey(format).ValueOrDie();
+  key.set_key_value("0123456789abcdef");
+  EXPECT_THAT(AesCmacKeyManager().ValidateKey(key), Not(IsOk()));
+}
+
+TEST(AesCmacKeyManagerTest, ValidateKeyLongTagSize) {
+  AesCmacKeyFormat format = ValidKeyFormat();
+  AesCmacKey key = AesCmacKeyManager().CreateKey(format).ValueOrDie();
+  key.mutable_params()->set_tag_size(17);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKey(key), Not(IsOk()));
+}
+
+
+TEST(AesCmacKeyManagerTest, ValidateKeyTooShortTagSize) {
+  AesCmacKeyFormat format = ValidKeyFormat();
+  AesCmacKey key = AesCmacKeyManager().CreateKey(format).ValueOrDie();
+  key.mutable_params()->set_tag_size(9);
+  EXPECT_THAT(AesCmacKeyManager().ValidateKey(key), Not(IsOk()));
 }
 
-TEST_F(AesCmacKeyManagerTest, testNewKeyBasic) {
-  AesCmacKeyManager key_manager;
-  const KeyFactory& key_factory = key_manager.get_key_factory();
-  AesCmacKeyFormat key_format;
-  key_format.set_key_size(32);
-  key_format.mutable_params()->set_tag_size(16);
-
-  { // Via NewKey(format_proto).
-    auto result = key_factory.NewKey(key_format);
-    EXPECT_TRUE(result.ok()) << result.status();
-    auto key = std::move(result.ValueOrDie());
-    EXPECT_EQ(key_type_prefix_ + key->GetTypeName(), cmac_key_type_);
-    std::unique_ptr<AesCmacKey> cmac_key(
-        static_cast<AesCmacKey*>(key.release()));
-    EXPECT_EQ(0, cmac_key->version());
-    EXPECT_EQ(16, cmac_key->params().tag_size());
-    EXPECT_EQ(key_format.key_size(), cmac_key->key_value().size());
-  }
-
-  { // Via NewKey(serialized_format_proto).
-    auto result = key_factory.NewKey(key_format.SerializeAsString());
-    EXPECT_TRUE(result.ok()) << result.status();
-    auto key = std::move(result.ValueOrDie());
-    EXPECT_EQ(key_type_prefix_ + key->GetTypeName(), cmac_key_type_);
-    std::unique_ptr<AesCmacKey> cmac_key(
-        static_cast<AesCmacKey*>(key.release()));
-    EXPECT_EQ(0, cmac_key->version());
-    EXPECT_EQ(16, cmac_key->params().tag_size());
-    EXPECT_EQ(key_format.key_size(), cmac_key->key_value().size());
-  }
-
-  { // Via NewKeyData(serialized_format_proto).
-    auto result = key_factory.NewKeyData(key_format.SerializeAsString());
-    EXPECT_TRUE(result.ok()) << result.status();
-    auto key_data = std::move(result.ValueOrDie());
-    EXPECT_EQ(cmac_key_type_, key_data->type_url());
-    EXPECT_EQ(KeyData::SYMMETRIC, key_data->key_material_type());
-    AesCmacKey cmac_key;
-    EXPECT_TRUE(cmac_key.ParseFromString(key_data->value()));
-    EXPECT_EQ(0, cmac_key.version());
-    EXPECT_EQ(16, cmac_key.params().tag_size());
-    EXPECT_EQ(key_format.key_size(), cmac_key.key_value().size());
-  }
+TEST(AesCmacKeyManagerTest, GetPrimitive) {
+  AesCmacKeyFormat format = ValidKeyFormat();
+  AesCmacKey key = AesCmacKeyManager().CreateKey(format).ValueOrDie();
+  auto manager_mac_or = AesCmacKeyManager().GetPrimitive<Mac>(key);
+  ASSERT_THAT(manager_mac_or.status(), IsOk());
+  auto mac_value_or = manager_mac_or.ValueOrDie()->ComputeMac("some plaintext");
+  ASSERT_THAT(mac_value_or.status(), IsOk());
+
+  auto direct_mac_or =
+      subtle::AesCmacBoringSsl::New(key.key_value(), key.params().tag_size());
+  ASSERT_THAT(direct_mac_or.status(), IsOk());
+  EXPECT_THAT(direct_mac_or.ValueOrDie()->VerifyMac(mac_value_or.ValueOrDie(),
+                                                    "some plaintext"), IsOk());
 }
 
 }  // namespace
diff --git a/cc/mac/mac_config.cc b/cc/mac/mac_config.cc
index f9fe25137..793d11285 100644
--- a/cc/mac/mac_config.cc
+++ b/cc/mac/mac_config.cc
@@ -45,7 +45,7 @@ util::Status MacConfig::Register() {
   auto status = Registry::RegisterKeyManager(
       absl::make_unique<HmacKeyManager>(), true);
   if (!status.ok()) return status;
-  status = Registry::RegisterKeyManager(
+  status = Registry::RegisterKeyTypeManager(
       absl::make_unique<AesCmacKeyManager>(), true);
   if (!status.ok()) return status;
 
diff --git a/cc/mac/mac_key_templates_test.cc b/cc/mac/mac_key_templates_test.cc
index 709850d9f..333fb319f 100644
--- a/cc/mac/mac_key_templates_test.cc
+++ b/cc/mac/mac_key_templates_test.cc
@@ -16,9 +16,11 @@
 
 #include "tink/mac/mac_key_templates.h"
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "tink/mac/aes_cmac_key_manager.h"
 #include "tink/mac/hmac_key_manager.h"
+#include "tink/util/test_matchers.h"
 #include "proto/aes_cmac.pb.h"
 #include "proto/common.pb.h"
 #include "proto/hmac.pb.h"
@@ -28,11 +30,14 @@ namespace crypto {
 namespace tink {
 namespace {
 
-using google::crypto::tink::AesCmacKeyFormat;
-using google::crypto::tink::HashType;
-using google::crypto::tink::HmacKeyFormat;
-using google::crypto::tink::KeyTemplate;
-using google::crypto::tink::OutputPrefixType;
+using ::crypto::tink::test::IsOk;
+using ::google::crypto::tink::AesCmacKeyFormat;
+using ::google::crypto::tink::HashType;
+using ::google::crypto::tink::HmacKeyFormat;
+using ::google::crypto::tink::KeyTemplate;
+using ::google::crypto::tink::OutputPrefixType;
+using ::testing::Eq;
+using ::testing::Ref;
 
 TEST(MacKeyTemplatesTest, testHmacKeyTemplates) {
   std::string type_url = "type.googleapis.com/google.crypto.tink.HmacKey";
@@ -128,26 +133,33 @@ TEST(MacKeyTemplatesTest, testHmacKeyTemplates) {
   }
 }
 
-TEST(MacKeyTemplatesTest, testAesCmacKeyTemplates) {
-  std::string type_url = "type.googleapis.com/google.crypto.tink.AesCmacKey";
+TEST(AesCmac, Basics) {
+  EXPECT_THAT(MacKeyTemplates::AesCmac().type_url(),
+              Eq("type.googleapis.com/google.crypto.tink.AesCmacKey"));
+  EXPECT_THAT(MacKeyTemplates::AesCmac().type_url(),
+              Eq(AesCmacKeyManager().get_key_type()));
+}
+
+TEST(AesCmac, OutputPrefixType) {
+  EXPECT_THAT(MacKeyTemplates::AesCmac().output_prefix_type(),
+              Eq(OutputPrefixType::TINK));
+}
+
+TEST(AesCmac, MultipleCallsSameReference) {
+  EXPECT_THAT(MacKeyTemplates::AesCmac(), Ref(MacKeyTemplates::AesCmac()));
+}
+
+TEST(AesCmac, WorksWithKeyTypeManager) {
+  AesCmacKeyFormat key_format;
+  EXPECT_TRUE(key_format.ParseFromString(MacKeyTemplates::AesCmac().value()));
+  EXPECT_THAT(AesCmacKeyManager().ValidateKeyFormat(key_format), IsOk());
+}
 
-  const KeyTemplate& key_template = MacKeyTemplates::AesCmac();
-  EXPECT_EQ(type_url, key_template.type_url());
-  EXPECT_EQ(OutputPrefixType::TINK, key_template.output_prefix_type());
+TEST(AesCmac, CheckValues) {
   AesCmacKeyFormat key_format;
-  EXPECT_TRUE(key_format.ParseFromString(key_template.value()));
-  EXPECT_EQ(32, key_format.key_size());
-  EXPECT_EQ(16, key_format.params().tag_size());
-
-  // Check that reference to the same object is returned.
-  const KeyTemplate& key_template_2 = MacKeyTemplates::AesCmac();
-  EXPECT_EQ(&key_template, &key_template_2);
-
-  // Check that the template works with the key manager.
-  AesCmacKeyManager key_manager;
-  EXPECT_EQ(key_manager.get_key_type(), key_template.type_url());
-  auto new_key_result = key_manager.get_key_factory().NewKey(key_format);
-  EXPECT_TRUE(new_key_result.ok()) << new_key_result.status();
+  EXPECT_TRUE(key_format.ParseFromString(MacKeyTemplates::AesCmac().value()));
+  EXPECT_THAT(key_format.key_size(), Eq(32));
+  EXPECT_THAT(key_format.params().tag_size(), Eq(16));
 }
 
 }  // namespace
-- 
GitLab