Add AesCmacParameters C++ classes.
PiperOrigin-RevId: 463096153
diff --git a/cc/mac/BUILD.bazel b/cc/mac/BUILD.bazel
index 5f6f3d8..b681672 100644
--- a/cc/mac/BUILD.bazel
+++ b/cc/mac/BUILD.bazel
@@ -151,6 +151,32 @@
],
)
+cc_library(
+ name = "failing_mac",
+ testonly = 1,
+ srcs = ["failing_mac.cc"],
+ hdrs = ["failing_mac.h"],
+ include_prefix = "tink/mac",
+ visibility = ["//visibility:public"],
+ deps = [
+ "//:mac",
+ "@com_google_absl//absl/strings",
+ ],
+)
+
+cc_library(
+ name = "aes_cmac_parameters",
+ srcs = ["aes_cmac_parameters.cc"],
+ hdrs = ["aes_cmac_parameters.h"],
+ include_prefix = "tink/mac",
+ deps = [
+ ":mac_parameters",
+ "//:crypto_format",
+ "//util:status",
+ "//util:statusor",
+ ],
+)
+
# tests
cc_test(
@@ -276,19 +302,6 @@
],
)
-cc_library(
- name = "failing_mac",
- testonly = 1,
- srcs = ["failing_mac.cc"],
- hdrs = ["failing_mac.h"],
- include_prefix = "tink/mac",
- visibility = ["//visibility:public"],
- deps = [
- "//:mac",
- "@com_google_absl//absl/strings",
- ],
-)
-
cc_test(
name = "failing_mac_test",
srcs = ["failing_mac_test.cc"],
@@ -299,3 +312,15 @@
"@com_google_googletest//:gtest_main",
],
)
+
+cc_test(
+ name = "aes_cmac_parameters_test",
+ size = "small",
+ srcs = ["aes_cmac_parameters_test.cc"],
+ deps = [
+ ":aes_cmac_parameters",
+ "//util:statusor",
+ "//util:test_matchers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/cc/mac/CMakeLists.txt b/cc/mac/CMakeLists.txt
index f0808da..ab763f1 100644
--- a/cc/mac/CMakeLists.txt
+++ b/cc/mac/CMakeLists.txt
@@ -143,6 +143,28 @@
tink::util::statusor
)
+tink_cc_library(
+ NAME failing_mac
+ SRCS
+ failing_mac.cc
+ failing_mac.h
+ DEPS
+ absl::strings
+ tink::core::mac
+)
+
+tink_cc_library(
+ NAME aes_cmac_parameters
+ SRCS
+ aes_cmac_parameters.cc
+ aes_cmac_parameters.h
+ DEPS
+ tink::mac::mac_parameters
+ tink::core::crypto_format
+ tink::util::status
+ tink::util::statusor
+)
+
# tests
tink_cc_test(
@@ -261,16 +283,6 @@
tink::proto::hmac_cc_proto
)
-tink_cc_library(
- NAME failing_mac
- SRCS
- failing_mac.cc
- failing_mac.h
- DEPS
- absl::strings
- tink::core::mac
-)
-
tink_cc_test(
NAME failing_mac_test
SRCS
@@ -281,3 +293,14 @@
absl::status
tink::util::test_matchers
)
+
+tink_cc_test(
+ NAME aes_cmac_parameters_test
+ SRCS
+ aes_cmac_parameters_test.cc
+ DEPS
+ tink::mac::aes_cmac_parameters
+ gmock
+ tink::util::statusor
+ tink::util::test_matchers
+)
diff --git a/cc/mac/aes_cmac_parameters.cc b/cc/mac/aes_cmac_parameters.cc
new file mode 100644
index 0000000..c4d8429
--- /dev/null
+++ b/cc/mac/aes_cmac_parameters.cc
@@ -0,0 +1,84 @@
+// 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/aes_cmac_parameters.h"
+
+#include <cstdlib>
+#include <iostream>
+#include <memory>
+#include <ostream>
+#include <set>
+
+#include "tink/crypto_format.h"
+#include "tink/util/status.h"
+#include "tink/util/statusor.h"
+
+namespace crypto {
+namespace tink {
+
+util::StatusOr<AesCmacParameters> AesCmacParameters::Create(
+ int cryptographic_tag_size_in_bytes, Variant variant) {
+ if (cryptographic_tag_size_in_bytes < 10) {
+ return util::Status(
+ absl::StatusCode::kInvalidArgument,
+ absl::StrCat("Tag size should be at least 10 bytes, got ",
+ cryptographic_tag_size_in_bytes, " bytes."));
+ }
+ if (cryptographic_tag_size_in_bytes > 16) {
+ return util::Status(
+ absl::StatusCode::kInvalidArgument,
+ absl::StrCat("Tag size should not exceed 16 bytes, got ",
+ cryptographic_tag_size_in_bytes, " bytes."));
+ }
+ static const std::set<Variant>* supported_variants =
+ new std::set<Variant>({Variant::kTink, Variant::kCrunchy,
+ Variant::kLegacy, Variant::kNoPrefix});
+ if (supported_variants->find(variant) == supported_variants->end()) {
+ return util::Status(
+ absl::StatusCode::kInvalidArgument,
+ "Cannot create AES-CMAC parameters with unknown variant.");
+ }
+ return AesCmacParameters(cryptographic_tag_size_in_bytes, variant);
+}
+
+int AesCmacParameters::TotalTagSizeInBytes() const {
+ switch (variant_) {
+ case Variant::kTink:
+ case Variant::kCrunchy:
+ case Variant::kLegacy:
+ return CryptographicTagSizeInBytes() + CryptoFormat::kNonRawPrefixSize;
+ case Variant::kNoPrefix:
+ return CryptographicTagSizeInBytes();
+ default:
+ // Parameters objects with unknown variants should never be created.
+ std::cerr << "AES-CMAC parameters has an unknown variant." << std::endl;
+ std::exit(1);
+ }
+}
+
+bool AesCmacParameters::operator==(const Parameters& other) const {
+ const AesCmacParameters* that =
+ dynamic_cast<const AesCmacParameters*>(&other);
+ if (that == nullptr) {
+ return false;
+ }
+ return cryptographic_tag_size_in_bytes_ ==
+ that->cryptographic_tag_size_in_bytes_ &&
+ variant_ == that->variant_;
+}
+
+} // namespace tink
+} // namespace crypto
diff --git a/cc/mac/aes_cmac_parameters.h b/cc/mac/aes_cmac_parameters.h
new file mode 100644
index 0000000..a4a09a7
--- /dev/null
+++ b/cc/mac/aes_cmac_parameters.h
@@ -0,0 +1,93 @@
+// 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef TINK_MAC_AES_CMAC_PARAMETERS_H_
+#define TINK_MAC_AES_CMAC_PARAMETERS_H_
+
+#include <memory>
+
+#include "tink/mac/mac_parameters.h"
+#include "tink/util/statusor.h"
+
+namespace crypto {
+namespace tink {
+
+// Describes the parameters of an `AesCmacKey`.
+class AesCmacParameters : public MacParameters {
+ public:
+ // Describes the details of a MAC computation.
+ //
+ // The usual AES-CMAC key is used for variant `NO_PREFIX`. Other variants
+ // slightly change how the MAC is computed, or add a prefix to every
+ // computation depending on the key id.
+ enum class Variant : int {
+ // Prepends '0x01<big endian key id>' to tag.
+ kTink = 1,
+ // Prepends '0x00<big endian key id>' to tag.
+ kCrunchy = 2,
+ // Appends a 0-byte to input message BEFORE computing the tag, then
+ // prepends '0x00<big endian key id>' to tag.
+ kLegacy = 3,
+ // Does not prepend any prefix (i.e., keys must have no ID requirement).
+ kNoPrefix = 4,
+ // Added to guard from failures that may be caused by future expansions.
+ kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements = 20,
+ };
+
+ // Copyable and movable.
+ AesCmacParameters(const AesCmacParameters& other) = default;
+ AesCmacParameters& operator=(const AesCmacParameters& other) = default;
+ AesCmacParameters(AesCmacParameters&& other) = default;
+ AesCmacParameters& operator=(AesCmacParameters&& other) = default;
+
+ // Creates a new AES-CMAC parameters object. Returns an error status if
+ // `cryptographic_tag_size_in_bytes` falls outside [10,...,16]. Otherwise,
+ // returns the parameters object.
+ static util::StatusOr<AesCmacParameters> Create(
+ int cryptographic_tag_size_in_bytes, Variant variant);
+
+ Variant GetVariant() const { return variant_; }
+
+ // Returns the size of the tag, which is computed cryptographically from the
+ // message. Note that this may differ from the total size of the tag, as for
+ // some keys, Tink prefixes the tag with a key dependent output prefix.
+ int CryptographicTagSizeInBytes() const {
+ return cryptographic_tag_size_in_bytes_;
+ }
+
+ // Returns the size of the cryptographic tag plus the size of the prefix with
+ // which this key prefixes every cryptographic tag.
+ int TotalTagSizeInBytes() const;
+
+ bool HasIdRequirement() const override {
+ return variant_ != Variant::kNoPrefix;
+ }
+
+ bool operator==(const Parameters& other) const override;
+
+ private:
+ AesCmacParameters(int cryptographic_tag_size_in_bytes, Variant variant)
+ : cryptographic_tag_size_in_bytes_(cryptographic_tag_size_in_bytes),
+ variant_(variant) {}
+
+ int cryptographic_tag_size_in_bytes_;
+ Variant variant_;
+};
+
+} // namespace tink
+} // namespace crypto
+
+#endif // TINK_MAC_AES_CMAC_PARAMETERS_H_
diff --git a/cc/mac/aes_cmac_parameters_test.cc b/cc/mac/aes_cmac_parameters_test.cc
new file mode 100644
index 0000000..db1f2c3
--- /dev/null
+++ b/cc/mac/aes_cmac_parameters_test.cc
@@ -0,0 +1,203 @@
+// 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/aes_cmac_parameters.h"
+
+#include <memory>
+#include <tuple>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "tink/util/statusor.h"
+#include "tink/util/test_matchers.h"
+
+namespace crypto {
+namespace tink {
+namespace {
+
+using ::crypto::tink::test::IsOk;
+using ::crypto::tink::test::StatusIs;
+using ::testing::Combine;
+using ::testing::Eq;
+using ::testing::Range;
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+struct CreateTestCase {
+ AesCmacParameters::Variant variant;
+ int cryptographic_tag_size_in_bytes;
+ int total_tag_size_in_bytes;
+ bool has_id_requirement;
+};
+
+class AesCmacParametersCreateTest : public TestWithParam<CreateTestCase> {};
+
+INSTANTIATE_TEST_SUITE_P(
+ AesCmacParametersCreateTestSuite, AesCmacParametersCreateTest,
+ Values(CreateTestCase{AesCmacParameters::Variant::kTink, 10, 15, true},
+ CreateTestCase{AesCmacParameters::Variant::kCrunchy, 12, 17, true},
+ CreateTestCase{AesCmacParameters::Variant::kLegacy, 14, 19, true},
+ CreateTestCase{AesCmacParameters::Variant::kNoPrefix, 16, 16,
+ false}));
+
+TEST_P(AesCmacParametersCreateTest, Create) {
+ CreateTestCase test_case = GetParam();
+
+ util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create(
+ test_case.cryptographic_tag_size_in_bytes, test_case.variant);
+ ASSERT_THAT(parameters, IsOk());
+
+ EXPECT_THAT(parameters->GetVariant(), Eq(test_case.variant));
+ EXPECT_THAT(parameters->CryptographicTagSizeInBytes(),
+ Eq(test_case.cryptographic_tag_size_in_bytes));
+ EXPECT_THAT(parameters->TotalTagSizeInBytes(),
+ Eq(test_case.total_tag_size_in_bytes));
+ EXPECT_THAT(parameters->HasIdRequirement(), Eq(test_case.has_id_requirement));
+}
+
+TEST(AesCmacParametersTest, CreateWithInvalidVariantFails) {
+ EXPECT_THAT(AesCmacParameters::Create(
+ /*cryptographic_tag_size_in_bytes=*/12,
+ AesCmacParameters::Variant::
+ kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements)
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+}
+
+TEST(AesCmacParametersTest, CreateWithInvalidTagSizeFails) {
+ // Too small.
+ EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/7,
+ AesCmacParameters::Variant::kNoPrefix)
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+ EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/8,
+ AesCmacParameters::Variant::kNoPrefix)
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+ EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/9,
+ AesCmacParameters::Variant::kNoPrefix)
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+ // Too big;
+ EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/17,
+ AesCmacParameters::Variant::kNoPrefix)
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+ EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/18,
+ AesCmacParameters::Variant::kNoPrefix)
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+ EXPECT_THAT(AesCmacParameters::Create(/*cryptographic_tag_size_in_bytes=*/19,
+ AesCmacParameters::Variant::kNoPrefix)
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+}
+
+TEST(AesCmacParametersTest, CopyConstructor) {
+ util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create(
+ /*cryptographic_tag_size_in_bytes=*/12,
+ AesCmacParameters::Variant::kTink);
+ ASSERT_THAT(parameters, IsOk());
+
+ AesCmacParameters copy(*parameters);
+ EXPECT_THAT(copy.GetVariant(), Eq(parameters->GetVariant()));
+ EXPECT_THAT(copy.CryptographicTagSizeInBytes(),
+ Eq(parameters->CryptographicTagSizeInBytes()));
+ EXPECT_THAT(copy.TotalTagSizeInBytes(),
+ Eq(parameters->TotalTagSizeInBytes()));
+ EXPECT_THAT(copy.HasIdRequirement(), Eq(parameters->HasIdRequirement()));
+}
+
+TEST(AesCmacParametersTest, CopyAssignment) {
+ util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create(
+ /*cryptographic_tag_size_in_bytes=*/12,
+ AesCmacParameters::Variant::kTink);
+ ASSERT_THAT(parameters, IsOk());
+
+ AesCmacParameters copy = *parameters;
+ EXPECT_THAT(copy.GetVariant(), Eq(parameters->GetVariant()));
+ EXPECT_THAT(copy.CryptographicTagSizeInBytes(),
+ Eq(parameters->CryptographicTagSizeInBytes()));
+ EXPECT_THAT(copy.TotalTagSizeInBytes(),
+ Eq(parameters->TotalTagSizeInBytes()));
+ EXPECT_THAT(copy.HasIdRequirement(), Eq(parameters->HasIdRequirement()));
+}
+
+class AesCmacParametersVariantTest
+ : public TestWithParam<std::tuple<AesCmacParameters::Variant, int>> {};
+
+INSTANTIATE_TEST_SUITE_P(AesCmacParametersVariantTestSuite,
+ AesCmacParametersVariantTest,
+ Combine(Values(AesCmacParameters::Variant::kTink,
+ AesCmacParameters::Variant::kCrunchy,
+ AesCmacParameters::Variant::kLegacy,
+ AesCmacParameters::Variant::kNoPrefix),
+ Range(10, 16)));
+
+TEST_P(AesCmacParametersVariantTest, ParametersEquals) {
+ AesCmacParameters::Variant variant;
+ int cryptographic_tag_size;
+ std::tie(variant, cryptographic_tag_size) = GetParam();
+
+ util::StatusOr<AesCmacParameters> parameters =
+ AesCmacParameters::Create(cryptographic_tag_size, variant);
+ ASSERT_THAT(parameters, IsOk());
+
+ util::StatusOr<AesCmacParameters> other_parameters =
+ AesCmacParameters::Create(cryptographic_tag_size, variant);
+ ASSERT_THAT(other_parameters, IsOk());
+
+ EXPECT_TRUE(*parameters == *other_parameters);
+ EXPECT_TRUE(*other_parameters == *parameters);
+ EXPECT_FALSE(*parameters != *other_parameters);
+ EXPECT_FALSE(*other_parameters != *parameters);
+}
+
+TEST(AesCmacParametersTest, TagSizeNotEqual) {
+ util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create(
+ /*cryptographic_tag_size_in_bytes=*/10,
+ AesCmacParameters::Variant::kNoPrefix);
+ ASSERT_THAT(parameters, IsOk());
+
+ util::StatusOr<AesCmacParameters> other_parameters =
+ AesCmacParameters::Create(
+ /*cryptographic_tag_size_in_bytes=*/11,
+ AesCmacParameters::Variant::kNoPrefix);
+ ASSERT_THAT(other_parameters, IsOk());
+
+ EXPECT_TRUE(*parameters != *other_parameters);
+ EXPECT_FALSE(*parameters == *other_parameters);
+}
+
+TEST(AesCmacParametersTest, VariantNotEqual) {
+ util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create(
+ /*cryptographic_tag_size_in_bytes=*/10,
+ AesCmacParameters::Variant::kNoPrefix);
+ ASSERT_THAT(parameters, IsOk());
+
+ util::StatusOr<AesCmacParameters> other_parameters =
+ AesCmacParameters::Create(
+ /*cryptographic_tag_size_in_bytes=*/10,
+ AesCmacParameters::Variant::kTink);
+ ASSERT_THAT(other_parameters, IsOk());
+
+ EXPECT_TRUE(*parameters != *other_parameters);
+ EXPECT_FALSE(*parameters == *other_parameters);
+}
+
+} // namespace
+} // namespace tink
+} // namespace crypto