aboutsummaryrefslogtreecommitdiff
path: root/cc/daead
diff options
context:
space:
mode:
authorwconner <wconner@google.com>2023-06-15 06:10:30 -0700
committerCopybara-Service <copybara-worker@google.com>2023-06-15 06:11:51 -0700
commit2e7c8390c1ea2f14399d573d03aa3df438fa6c69 (patch)
tree6ff6cda2842bd4d5a0aab268cc961a5809b967df /cc/daead
parent5d13c5cdf12576959aecc5472d45d4056134811c (diff)
downloadtink-2e7c8390c1ea2f14399d573d03aa3df438fa6c69.tar.gz
Add AES-SIV C++ key type.
PiperOrigin-RevId: 540560055
Diffstat (limited to 'cc/daead')
-rw-r--r--cc/daead/BUILD.bazel33
-rw-r--r--cc/daead/CMakeLists.txt32
-rw-r--r--cc/daead/aes_siv_key.cc107
-rw-r--r--cc/daead/aes_siv_key.h83
-rw-r--r--cc/daead/aes_siv_key_test.cc230
5 files changed, 485 insertions, 0 deletions
diff --git a/cc/daead/BUILD.bazel b/cc/daead/BUILD.bazel
index f4452a78d..97427828a 100644
--- a/cc/daead/BUILD.bazel
+++ b/cc/daead/BUILD.bazel
@@ -137,6 +137,24 @@ cc_library(
],
)
+cc_library(
+ name = "aes_siv_key",
+ srcs = ["aes_siv_key.cc"],
+ hdrs = ["aes_siv_key.h"],
+ include_prefix = "tink/daead",
+ deps = [
+ ":aes_siv_parameters",
+ ":deterministic_aead_key",
+ "//:partial_key_access_token",
+ "//:restricted_data",
+ "//subtle:subtle_util",
+ "//util:status",
+ "//util:statusor",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/types:optional",
+ ],
+)
+
# tests
cc_test(
@@ -255,3 +273,18 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
+
+cc_test(
+ name = "aes_siv_key_test",
+ srcs = ["aes_siv_key_test.cc"],
+ deps = [
+ ":aes_siv_key",
+ ":aes_siv_parameters",
+ "//:partial_key_access",
+ "//:restricted_data",
+ "//util:statusor",
+ "//util:test_matchers",
+ "@com_google_absl//absl/types:optional",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/cc/daead/CMakeLists.txt b/cc/daead/CMakeLists.txt
index e944696fb..3a1efc047 100644
--- a/cc/daead/CMakeLists.txt
+++ b/cc/daead/CMakeLists.txt
@@ -128,6 +128,23 @@ tink_cc_library(
tink::util::statusor
)
+tink_cc_library(
+ NAME aes_siv_key
+ SRCS
+ aes_siv_key.cc
+ aes_siv_key.h
+ DEPS
+ tink::daead::aes_siv_parameters
+ tink::daead::deterministic_aead_key
+ absl::strings
+ absl::optional
+ tink::core::partial_key_access_token
+ tink::core::restricted_data
+ tink::subtle::subtle_util
+ tink::util::status
+ tink::util::statusor
+)
+
# tests
tink_cc_test(
@@ -240,3 +257,18 @@ tink_cc_test(
tink::util::statusor
tink::util::test_matchers
)
+
+tink_cc_test(
+ NAME aes_siv_key_test
+ SRCS
+ aes_siv_key_test.cc
+ DEPS
+ tink::daead::aes_siv_key
+ tink::daead::aes_siv_parameters
+ gmock
+ absl::optional
+ tink::core::partial_key_access
+ tink::core::restricted_data
+ tink::util::statusor
+ tink::util::test_matchers
+)
diff --git a/cc/daead/aes_siv_key.cc b/cc/daead/aes_siv_key.cc
new file mode 100644
index 000000000..00582b24d
--- /dev/null
+++ b/cc/daead/aes_siv_key.cc
@@ -0,0 +1,107 @@
+// Copyright 2023 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/daead/aes_siv_key.h"
+
+#include <string>
+
+#include "absl/strings/escaping.h"
+#include "absl/types/optional.h"
+#include "tink/daead/aes_siv_parameters.h"
+#include "tink/partial_key_access_token.h"
+#include "tink/restricted_data.h"
+#include "tink/subtle/subtle_util.h"
+#include "tink/util/status.h"
+#include "tink/util/statusor.h"
+
+namespace crypto {
+namespace tink {
+namespace {
+
+util::StatusOr<std::string> ComputeOutputPrefix(
+ const AesSivParameters& parameters, absl::optional<int> id_requirement) {
+ switch (parameters.GetVariant()) {
+ case AesSivParameters::Variant::kNoPrefix:
+ return std::string(""); // Empty prefix.
+ case AesSivParameters::Variant::kCrunchy:
+ if (!id_requirement.has_value()) {
+ return util::Status(
+ absl::StatusCode::kInvalidArgument,
+ "id requirement must have value with kCrunchy or kLegacy");
+ }
+ return absl::StrCat(absl::HexStringToBytes("00"),
+ subtle::BigEndian32(*id_requirement));
+ case AesSivParameters::Variant::kTink:
+ if (!id_requirement.has_value()) {
+ return util::Status(absl::StatusCode::kInvalidArgument,
+ "id requirement must have value with kTink");
+ }
+ return absl::StrCat(absl::HexStringToBytes("01"),
+ subtle::BigEndian32(*id_requirement));
+ default:
+ return util::Status(
+ absl::StatusCode::kInvalidArgument,
+ absl::StrCat("Invalid variant: ", parameters.GetVariant()));
+ }
+}
+
+} // namespace
+
+util::StatusOr<AesSivKey> AesSivKey::Create(const AesSivParameters& parameters,
+ const RestrictedData& key_bytes,
+ absl::optional<int> id_requirement,
+ PartialKeyAccessToken token) {
+ if (parameters.KeySizeInBytes() != key_bytes.size()) {
+ return util::Status(absl::StatusCode::kInvalidArgument,
+ "Key size does not match AES-SIV parameters");
+ }
+ if (parameters.HasIdRequirement() && !id_requirement.has_value()) {
+ return util::Status(
+ absl::StatusCode::kInvalidArgument,
+ "Cannot create key without ID requirement with parameters with ID "
+ "requirement");
+ }
+ if (!parameters.HasIdRequirement() && id_requirement.has_value()) {
+ return util::Status(
+ absl::StatusCode::kInvalidArgument,
+ "Cannot create key with ID requirement with parameters without ID "
+ "requirement");
+ }
+ util::StatusOr<std::string> output_prefix =
+ ComputeOutputPrefix(parameters, id_requirement);
+ if (!output_prefix.ok()) {
+ return output_prefix.status();
+ }
+ return AesSivKey(parameters, key_bytes, id_requirement,
+ *std::move(output_prefix));
+}
+
+bool AesSivKey::operator==(const Key& other) const {
+ const AesSivKey* that = dynamic_cast<const AesSivKey*>(&other);
+ if (that == nullptr) {
+ return false;
+ }
+ if (GetParameters() != that->GetParameters()) {
+ return false;
+ }
+ if (id_requirement_ != that->id_requirement_) {
+ return false;
+ }
+ return key_bytes_ == that->key_bytes_;
+}
+
+} // namespace tink
+} // namespace crypto
diff --git a/cc/daead/aes_siv_key.h b/cc/daead/aes_siv_key.h
new file mode 100644
index 000000000..8ca060d71
--- /dev/null
+++ b/cc/daead/aes_siv_key.h
@@ -0,0 +1,83 @@
+// Copyright 2023 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_DAEAD_AES_SIV_KEY_H_
+#define TINK_DAEAD_AES_SIV_KEY_H_
+
+#include <string>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "tink/daead/aes_siv_parameters.h"
+#include "tink/daead/deterministic_aead_key.h"
+#include "tink/partial_key_access_token.h"
+#include "tink/restricted_data.h"
+#include "tink/util/statusor.h"
+
+namespace crypto {
+namespace tink {
+
+// Represents a Deterministic AEAD that uses AES-SIV.
+class AesSivKey : public DeterministicAeadKey {
+ public:
+ // Copyable and movable.
+ AesSivKey(const AesSivKey& other) = default;
+ AesSivKey& operator=(const AesSivKey& other) = default;
+ AesSivKey(AesSivKey&& other) = default;
+ AesSivKey& operator=(AesSivKey&& other) = default;
+
+ // Creates a new AES-SIV key. If the parameters specify a variant that uses
+ // a prefix, then the id is used to compute this prefix.
+ static util::StatusOr<AesSivKey> Create(const AesSivParameters& parameters,
+ const RestrictedData& key_bytes,
+ absl::optional<int> id_requirement,
+ PartialKeyAccessToken token);
+
+ // Returns the underlying AES-SIV key.
+ util::StatusOr<RestrictedData> GetKeyBytes(
+ PartialKeyAccessToken token) const {
+ return key_bytes_;
+ }
+
+ absl::string_view GetOutputPrefix() const override { return output_prefix_; }
+
+ const AesSivParameters& GetParameters() const override { return parameters_; }
+
+ absl::optional<int> GetIdRequirement() const override {
+ return id_requirement_;
+ }
+
+ bool operator==(const Key& other) const override;
+
+ private:
+ AesSivKey(const AesSivParameters& parameters, const RestrictedData& key_bytes,
+ absl::optional<int> id_requirement, std::string output_prefix)
+ : parameters_(parameters),
+ key_bytes_(key_bytes),
+ id_requirement_(id_requirement),
+ output_prefix_(std::move(output_prefix)) {}
+
+ AesSivParameters parameters_;
+ RestrictedData key_bytes_;
+ absl::optional<int> id_requirement_;
+ std::string output_prefix_;
+};
+
+} // namespace tink
+} // namespace crypto
+
+#endif // TINK_DAEAD_AES_SIV_KEY_H_
diff --git a/cc/daead/aes_siv_key_test.cc b/cc/daead/aes_siv_key_test.cc
new file mode 100644
index 000000000..294525e47
--- /dev/null
+++ b/cc/daead/aes_siv_key_test.cc
@@ -0,0 +1,230 @@
+// Copyright 2023 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/daead/aes_siv_key.h"
+
+#include <string>
+#include <tuple>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/types/optional.h"
+#include "tink/daead/aes_siv_parameters.h"
+#include "tink/partial_key_access.h"
+#include "tink/restricted_data.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::IsOkAndHolds;
+using ::crypto::tink::test::StatusIs;
+using ::testing::Combine;
+using ::testing::Eq;
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+struct TestCase {
+ AesSivParameters::Variant variant;
+ absl::optional<int> id_requirement;
+ std::string output_prefix;
+};
+
+using AesSivKeyTest = TestWithParam<std::tuple<int, TestCase>>;
+
+INSTANTIATE_TEST_SUITE_P(
+ AesSivKeyTestSuite, AesSivKeyTest,
+ Combine(Values(32, 48, 64),
+ Values(TestCase{AesSivParameters::Variant::kTink, 0x02030400,
+ std::string("\x01\x02\x03\x04\x00", 5)},
+ TestCase{AesSivParameters::Variant::kCrunchy, 0x01030005,
+ std::string("\x00\x01\x03\x00\x05", 5)},
+ TestCase{AesSivParameters::Variant::kNoPrefix, absl::nullopt,
+ ""})));
+
+TEST_P(AesSivKeyTest, CreateSucceeds) {
+ int key_size;
+ TestCase test_case;
+ std::tie(key_size, test_case) = GetParam();
+
+ util::StatusOr<AesSivParameters> params =
+ AesSivParameters::Create(key_size, test_case.variant);
+ ASSERT_THAT(params, IsOk());
+
+ RestrictedData secret = RestrictedData(key_size);
+ util::StatusOr<AesSivKey> key = AesSivKey::Create(
+ *params, secret, test_case.id_requirement, GetPartialKeyAccess());
+ ASSERT_THAT(key, IsOk());
+
+ EXPECT_THAT(key->GetParameters(), Eq(*params));
+ EXPECT_THAT(key->GetIdRequirement(), Eq(test_case.id_requirement));
+ EXPECT_THAT(key->GetOutputPrefix(), Eq(test_case.output_prefix));
+}
+
+TEST(AesSivKeyTest, CreateKeyWithMismatchedKeySizeFails) {
+ // Key size parameter is 64 bytes.
+ util::StatusOr<AesSivParameters> params = AesSivParameters::Create(
+ /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink);
+ ASSERT_THAT(params, IsOk());
+
+ // Key material is 32 bytes (another valid key length).
+ RestrictedData mismatched_secret = RestrictedData(/*num_random_bytes=*/32);
+
+ EXPECT_THAT(AesSivKey::Create(*params, mismatched_secret,
+ /*id_requirement=*/123, GetPartialKeyAccess())
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+}
+
+TEST(AesSivKeyTest, CreateKeyWithInvalidIdRequirementFails) {
+ util::StatusOr<AesSivParameters> no_prefix_params = AesSivParameters::Create(
+ /*key_size_in_bytes=*/64, AesSivParameters::Variant::kNoPrefix);
+ ASSERT_THAT(no_prefix_params, IsOk());
+
+ util::StatusOr<AesSivParameters> tink_params = AesSivParameters::Create(
+ /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink);
+ ASSERT_THAT(tink_params, IsOk());
+
+ RestrictedData secret = RestrictedData(/*num_random_bytes=*/64);
+
+ EXPECT_THAT(AesSivKey::Create(*no_prefix_params, secret,
+ /*id_requirement=*/123, GetPartialKeyAccess())
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+ EXPECT_THAT(
+ AesSivKey::Create(*tink_params, secret,
+ /*id_requirement=*/absl::nullopt, GetPartialKeyAccess())
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+}
+
+TEST_P(AesSivKeyTest, GetKeyBytes) {
+ int key_size;
+ TestCase test_case;
+ std::tie(key_size, test_case) = GetParam();
+
+ util::StatusOr<AesSivParameters> params =
+ AesSivParameters::Create(key_size, test_case.variant);
+ ASSERT_THAT(params, IsOk());
+
+ RestrictedData secret = RestrictedData(key_size);
+
+ util::StatusOr<AesSivKey> key = AesSivKey::Create(
+ *params, secret, test_case.id_requirement, GetPartialKeyAccess());
+ ASSERT_THAT(key, IsOk());
+ EXPECT_THAT(key->GetKeyBytes(GetPartialKeyAccess()), IsOkAndHolds(secret));
+}
+
+TEST_P(AesSivKeyTest, KeyEquals) {
+ int key_size;
+ TestCase test_case;
+ std::tie(key_size, test_case) = GetParam();
+
+ util::StatusOr<AesSivParameters> params =
+ AesSivParameters::Create(key_size, test_case.variant);
+ ASSERT_THAT(params, IsOk());
+
+ RestrictedData secret = RestrictedData(key_size);
+ util::StatusOr<AesSivKey> key = AesSivKey::Create(
+ *params, secret, test_case.id_requirement, GetPartialKeyAccess());
+ ASSERT_THAT(key, IsOk());
+
+ util::StatusOr<AesSivKey> other_key = AesSivKey::Create(
+ *params, secret, test_case.id_requirement, GetPartialKeyAccess());
+ ASSERT_THAT(other_key, IsOk());
+
+ EXPECT_TRUE(*key == *other_key);
+ EXPECT_TRUE(*other_key == *key);
+ EXPECT_FALSE(*key != *other_key);
+ EXPECT_FALSE(*other_key != *key);
+}
+
+TEST(AesSivKeyTest, DifferentVariantNotEqual) {
+ util::StatusOr<AesSivParameters> crunchy_params = AesSivParameters::Create(
+ /*key_size_in_bytes=*/64, AesSivParameters::Variant::kCrunchy);
+ ASSERT_THAT(crunchy_params, IsOk());
+
+ util::StatusOr<AesSivParameters> tink_params = AesSivParameters::Create(
+ /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink);
+ ASSERT_THAT(tink_params, IsOk());
+
+ RestrictedData secret = RestrictedData(/*num_random_bytes=*/64);
+
+ util::StatusOr<AesSivKey> key =
+ AesSivKey::Create(*crunchy_params, secret, /*id_requirement=*/0x01020304,
+ GetPartialKeyAccess());
+ ASSERT_THAT(key, IsOk());
+
+ util::StatusOr<AesSivKey> other_key =
+ AesSivKey::Create(*tink_params, secret, /*id_requirement=*/0x01020304,
+ GetPartialKeyAccess());
+ ASSERT_THAT(other_key, IsOk());
+
+ EXPECT_TRUE(*key != *other_key);
+ EXPECT_TRUE(*other_key != *key);
+ EXPECT_FALSE(*key == *other_key);
+ EXPECT_FALSE(*other_key == *key);
+}
+
+TEST(AesSivKeyTest, DifferentSecretDataNotEqual) {
+ util::StatusOr<AesSivParameters> params = AesSivParameters::Create(
+ /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink);
+ ASSERT_THAT(params, IsOk());
+
+ RestrictedData secret1 = RestrictedData(/*num_random_bytes=*/64);
+ RestrictedData secret2 = RestrictedData(/*num_random_bytes=*/64);
+
+ util::StatusOr<AesSivKey> key = AesSivKey::Create(
+ *params, secret1, /*id_requirement=*/0x01020304, GetPartialKeyAccess());
+ ASSERT_THAT(key, IsOk());
+
+ util::StatusOr<AesSivKey> other_key = AesSivKey::Create(
+ *params, secret2, /*id_requirement=*/0x01020304, GetPartialKeyAccess());
+ ASSERT_THAT(other_key, IsOk());
+
+ EXPECT_TRUE(*key != *other_key);
+ EXPECT_TRUE(*other_key != *key);
+ EXPECT_FALSE(*key == *other_key);
+ EXPECT_FALSE(*other_key == *key);
+}
+
+TEST(AesSivKeyTest, DifferentIdRequirementNotEqual) {
+ util::StatusOr<AesSivParameters> params = AesSivParameters::Create(
+ /*key_size_in_bytes=*/64, AesSivParameters::Variant::kTink);
+ ASSERT_THAT(params, IsOk());
+
+ RestrictedData secret = RestrictedData(/*num_random_bytes=*/64);
+
+ util::StatusOr<AesSivKey> key = AesSivKey::Create(
+ *params, secret, /*id_requirement=*/0x01020304, GetPartialKeyAccess());
+ ASSERT_THAT(key, IsOk());
+
+ util::StatusOr<AesSivKey> other_key = AesSivKey::Create(
+ *params, secret, /*id_requirement=*/0x02030405, GetPartialKeyAccess());
+ ASSERT_THAT(other_key, IsOk());
+
+ EXPECT_TRUE(*key != *other_key);
+ EXPECT_TRUE(*other_key != *key);
+ EXPECT_FALSE(*key == *other_key);
+ EXPECT_FALSE(*other_key == *key);
+}
+
+} // namespace
+} // namespace tink
+} // namespace crypto