aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwconner <wconner@google.com>2023-08-01 07:03:34 -0700
committerCopybara-Service <copybara-worker@google.com>2023-08-01 07:05:41 -0700
commit3d025c6568b892514bdebde87102405ab5c79369 (patch)
tree232feead7ce0fe73160826475804f62296f25a8b
parent67ffa92455c836967cf02f5aa615310822cee42b (diff)
downloadtink-3d025c6568b892514bdebde87102405ab5c79369.tar.gz
Add Ed25519 public key type.
PiperOrigin-RevId: 552790750
-rw-r--r--cc/signature/BUILD.bazel31
-rw-r--r--cc/signature/CMakeLists.txt30
-rw-r--r--cc/signature/ed25519_public_key.cc108
-rw-r--r--cc/signature/ed25519_public_key.h81
-rw-r--r--cc/signature/ed25519_public_key_test.cc215
5 files changed, 465 insertions, 0 deletions
diff --git a/cc/signature/BUILD.bazel b/cc/signature/BUILD.bazel
index 7f31b48a8..22158a142 100644
--- a/cc/signature/BUILD.bazel
+++ b/cc/signature/BUILD.bazel
@@ -443,6 +443,24 @@ cc_library(
],
)
+cc_library(
+ name = "ed25519_public_key",
+ srcs = ["ed25519_public_key.cc"],
+ hdrs = ["ed25519_public_key.h"],
+ include_prefix = "tink/signature",
+ deps = [
+ ":ed25519_parameters",
+ ":signature_public_key",
+ "//:partial_key_access_token",
+ "//subtle:subtle_util",
+ "//util:status",
+ "//util:statusor",
+ "@com_google_absl//absl/base:core_headers",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/types:optional",
+ ],
+)
+
# tests
cc_test(
@@ -825,3 +843,16 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
+
+cc_test(
+ name = "ed25519_public_key_test",
+ srcs = ["ed25519_public_key_test.cc"],
+ deps = [
+ ":ed25519_public_key",
+ "//:partial_key_access",
+ "//subtle:random",
+ "//util:statusor",
+ "//util:test_matchers",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/cc/signature/CMakeLists.txt b/cc/signature/CMakeLists.txt
index 68a8fc6aa..8693762eb 100644
--- a/cc/signature/CMakeLists.txt
+++ b/cc/signature/CMakeLists.txt
@@ -422,6 +422,23 @@ tink_cc_library(
tink::util::statusor
)
+tink_cc_library(
+ NAME ed25519_public_key
+ SRCS
+ ed25519_public_key.cc
+ ed25519_public_key.h
+ DEPS
+ tink::signature::ed25519_parameters
+ tink::signature::signature_public_key
+ absl::core_headers
+ absl::strings
+ absl::optional
+ tink::core::partial_key_access_token
+ tink::subtle::subtle_util
+ tink::util::status
+ tink::util::statusor
+)
+
# tests
tink_cc_test(
@@ -788,3 +805,16 @@ tink_cc_test(
tink::util::statusor
tink::util::test_matchers
)
+
+tink_cc_test(
+ NAME ed25519_public_key_test
+ SRCS
+ ed25519_public_key_test.cc
+ DEPS
+ tink::signature::ed25519_public_key
+ gmock
+ tink::core::partial_key_access
+ tink::subtle::random
+ tink::util::statusor
+ tink::util::test_matchers
+)
diff --git a/cc/signature/ed25519_public_key.cc b/cc/signature/ed25519_public_key.cc
new file mode 100644
index 000000000..fce923933
--- /dev/null
+++ b/cc/signature/ed25519_public_key.cc
@@ -0,0 +1,108 @@
+// 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/signature/ed25519_public_key.h"
+
+#include <string>
+
+#include "absl/base/attributes.h"
+#include "absl/strings/escaping.h"
+#include "absl/types/optional.h"
+#include "tink/partial_key_access_token.h"
+#include "tink/signature/ed25519_parameters.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 Ed25519Parameters& parameters, absl::optional<int> id_requirement) {
+ switch (parameters.GetVariant()) {
+ case Ed25519Parameters::Variant::kNoPrefix:
+ return std::string(""); // Empty prefix.
+ case Ed25519Parameters::Variant::kLegacy:
+ ABSL_FALLTHROUGH_INTENDED;
+ case Ed25519Parameters::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 Ed25519Parameters::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<Ed25519PublicKey> Ed25519PublicKey::Create(
+ const Ed25519Parameters& parameters, absl::string_view public_key_bytes,
+ absl::optional<int> id_requirement, PartialKeyAccessToken token) {
+ 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");
+ }
+ if (public_key_bytes.size() != 32) {
+ return util::Status(absl::StatusCode::kInvalidArgument,
+ "Ed25519 public key length must be 32 bytes.");
+ }
+ util::StatusOr<std::string> output_prefix =
+ ComputeOutputPrefix(parameters, id_requirement);
+ if (!output_prefix.ok()) {
+ return output_prefix.status();
+ }
+ return Ed25519PublicKey(parameters, public_key_bytes, id_requirement,
+ *output_prefix);
+}
+
+bool Ed25519PublicKey::operator==(const Key& other) const {
+ const Ed25519PublicKey* that = dynamic_cast<const Ed25519PublicKey*>(&other);
+ if (that == nullptr) {
+ return false;
+ }
+ if (GetParameters() != that->GetParameters()) {
+ return false;
+ }
+ if (id_requirement_ != that->id_requirement_) {
+ return false;
+ }
+ return public_key_bytes_ == that->public_key_bytes_;
+}
+
+} // namespace tink
+} // namespace crypto
diff --git a/cc/signature/ed25519_public_key.h b/cc/signature/ed25519_public_key.h
new file mode 100644
index 000000000..5deae86a2
--- /dev/null
+++ b/cc/signature/ed25519_public_key.h
@@ -0,0 +1,81 @@
+// 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_SIGNATURE_ED25519_PUBLIC_KEY_H_
+#define TINK_SIGNATURE_ED25519_PUBLIC_KEY_H_
+
+#include <string>
+
+#include "absl/types/optional.h"
+#include "tink/partial_key_access_token.h"
+#include "tink/signature/ed25519_parameters.h"
+#include "tink/signature/signature_public_key.h"
+#include "tink/util/statusor.h"
+
+namespace crypto {
+namespace tink {
+
+class Ed25519PublicKey : public SignaturePublicKey {
+ public:
+ // Copyable and movable.
+ Ed25519PublicKey(const Ed25519PublicKey& other) = default;
+ Ed25519PublicKey& operator=(const Ed25519PublicKey& other) = default;
+ Ed25519PublicKey(Ed25519PublicKey&& other) = default;
+ Ed25519PublicKey& operator=(Ed25519PublicKey&& other) = default;
+
+ // Creates a new Ed25519 public key from `public_key_bytes`. If `parameters`
+ // specify a variant that uses a prefix, then `id_requirement` is used to
+ // compute this prefix.
+ static util::StatusOr<Ed25519PublicKey> Create(
+ const Ed25519Parameters& parameters, absl::string_view public_key_bytes,
+ absl::optional<int> id_requirement, PartialKeyAccessToken token);
+
+ absl::string_view GetPublicKeyBytes(PartialKeyAccessToken token) const {
+ return public_key_bytes_;
+ }
+
+ absl::string_view GetOutputPrefix() const override { return output_prefix_; }
+
+ const Ed25519Parameters& GetParameters() const override {
+ return parameters_;
+ }
+
+ absl::optional<int> GetIdRequirement() const override {
+ return id_requirement_;
+ }
+
+ bool operator==(const Key& other) const override;
+
+ private:
+ explicit Ed25519PublicKey(const Ed25519Parameters& parameters,
+ absl::string_view public_key_bytes,
+ absl::optional<int> id_requirement,
+ absl::string_view output_prefix)
+ : parameters_(parameters),
+ public_key_bytes_(public_key_bytes),
+ id_requirement_(id_requirement),
+ output_prefix_(output_prefix) {}
+
+ Ed25519Parameters parameters_;
+ std::string public_key_bytes_;
+ absl::optional<int> id_requirement_;
+ std::string output_prefix_;
+};
+
+} // namespace tink
+} // namespace crypto
+
+#endif // TINK_SIGNATURE_ED25519_PUBLIC_KEY_H_
diff --git a/cc/signature/ed25519_public_key_test.cc b/cc/signature/ed25519_public_key_test.cc
new file mode 100644
index 000000000..c96ad35cd
--- /dev/null
+++ b/cc/signature/ed25519_public_key_test.cc
@@ -0,0 +1,215 @@
+// 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/signature/ed25519_public_key.h"
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "tink/partial_key_access.h"
+#include "tink/subtle/random.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::Eq;
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+struct TestCase {
+ Ed25519Parameters::Variant variant;
+ absl::optional<int> id_requirement;
+ std::string output_prefix;
+};
+
+using Ed25519PublicKeyTest = TestWithParam<TestCase>;
+
+INSTANTIATE_TEST_SUITE_P(
+ Ed25519PublicKeyTestSuite, Ed25519PublicKeyTest,
+ Values(TestCase{Ed25519Parameters::Variant::kTink, 0x02030400,
+ std::string("\x01\x02\x03\x04\x00", 5)},
+ TestCase{Ed25519Parameters::Variant::kCrunchy, 0x01030005,
+ std::string("\x00\x01\x03\x00\x05", 5)},
+ TestCase{Ed25519Parameters::Variant::kLegacy, 0x07080910,
+ std::string("\x00\x07\x08\x09\x10", 5)},
+ TestCase{Ed25519Parameters::Variant::kNoPrefix, absl::nullopt, ""}));
+
+TEST_P(Ed25519PublicKeyTest, CreateSucceeds) {
+ TestCase test_case = GetParam();
+
+ util::StatusOr<Ed25519Parameters> params =
+ Ed25519Parameters::Create(test_case.variant);
+ ASSERT_THAT(params, IsOk());
+
+ std::string public_key_bytes = subtle::Random::GetRandomBytes(32);
+ util::StatusOr<Ed25519PublicKey> public_key =
+ Ed25519PublicKey::Create(*params, public_key_bytes,
+ test_case.id_requirement, GetPartialKeyAccess());
+ ASSERT_THAT(public_key, IsOk());
+
+ EXPECT_THAT(public_key->GetParameters(), Eq(*params));
+ EXPECT_THAT(public_key->GetIdRequirement(), Eq(test_case.id_requirement));
+ EXPECT_THAT(public_key->GetOutputPrefix(), Eq(test_case.output_prefix));
+ EXPECT_THAT(public_key->GetPublicKeyBytes(GetPartialKeyAccess()),
+ Eq(public_key_bytes));
+}
+
+TEST(Ed25519PublicKeyTest, CreateWithInvalidPublicKeyLength) {
+ util::StatusOr<Ed25519Parameters> params =
+ Ed25519Parameters::Create(Ed25519Parameters::Variant::kTink);
+ ASSERT_THAT(params, IsOk());
+
+ std::string public_key_bytes = subtle::Random::GetRandomBytes(31);
+
+ EXPECT_THAT(
+ Ed25519PublicKey::Create(*params, public_key_bytes,
+ /*id_requirement=*/123, GetPartialKeyAccess())
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+}
+
+TEST(Ed25519PublicKeyTest, CreateKeyWithInvalidIdRequirementFails) {
+ util::StatusOr<Ed25519Parameters> no_prefix_params =
+ Ed25519Parameters::Create(Ed25519Parameters::Variant::kNoPrefix);
+ ASSERT_THAT(no_prefix_params, IsOk());
+
+ util::StatusOr<Ed25519Parameters> tink_params =
+ Ed25519Parameters::Create(Ed25519Parameters::Variant::kTink);
+ ASSERT_THAT(tink_params, IsOk());
+
+ std::string public_key_bytes = subtle::Random::GetRandomBytes(32);
+
+ EXPECT_THAT(
+ Ed25519PublicKey::Create(*no_prefix_params, public_key_bytes,
+ /*id_requirement=*/123, GetPartialKeyAccess())
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+
+ EXPECT_THAT(Ed25519PublicKey::Create(*tink_params, public_key_bytes,
+ /*id_requirement=*/absl::nullopt,
+ GetPartialKeyAccess())
+ .status(),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+}
+
+TEST_P(Ed25519PublicKeyTest, KeyEquals) {
+ TestCase test_case = GetParam();
+
+ util::StatusOr<Ed25519Parameters> params =
+ Ed25519Parameters::Create(test_case.variant);
+ ASSERT_THAT(params, IsOk());
+
+ std::string public_key_bytes = subtle::Random::GetRandomBytes(32);
+
+ util::StatusOr<Ed25519PublicKey> public_key =
+ Ed25519PublicKey::Create(*params, public_key_bytes,
+ test_case.id_requirement, GetPartialKeyAccess());
+ ASSERT_THAT(public_key, IsOk());
+
+ util::StatusOr<Ed25519PublicKey> other_public_key =
+ Ed25519PublicKey::Create(*params, public_key_bytes,
+ test_case.id_requirement, GetPartialKeyAccess());
+ ASSERT_THAT(other_public_key, IsOk());
+
+ EXPECT_TRUE(*public_key == *other_public_key);
+ EXPECT_TRUE(*other_public_key == *public_key);
+ EXPECT_FALSE(*public_key != *other_public_key);
+ EXPECT_FALSE(*other_public_key != *public_key);
+}
+
+TEST(Ed25519PublicKeyTest, DifferentVariantNotEqual) {
+ util::StatusOr<Ed25519Parameters> crunchy_params =
+ Ed25519Parameters::Create(Ed25519Parameters::Variant::kCrunchy);
+ ASSERT_THAT(crunchy_params, IsOk());
+
+ util::StatusOr<Ed25519Parameters> tink_params =
+ Ed25519Parameters::Create(Ed25519Parameters::Variant::kTink);
+ ASSERT_THAT(tink_params, IsOk());
+
+ std::string public_key_bytes = subtle::Random::GetRandomBytes(32);
+
+ util::StatusOr<Ed25519PublicKey> public_key = Ed25519PublicKey::Create(
+ *crunchy_params, public_key_bytes, /*id_requirement=*/0x01020304,
+ GetPartialKeyAccess());
+ ASSERT_THAT(public_key, IsOk());
+
+ util::StatusOr<Ed25519PublicKey> other_public_key = Ed25519PublicKey::Create(
+ *tink_params, public_key_bytes, /*id_requirement=*/0x01020304,
+ GetPartialKeyAccess());
+ ASSERT_THAT(other_public_key, IsOk());
+
+ EXPECT_TRUE(*public_key != *other_public_key);
+ EXPECT_TRUE(*other_public_key != *public_key);
+ EXPECT_FALSE(*public_key == *other_public_key);
+ EXPECT_FALSE(*other_public_key == *public_key);
+}
+
+TEST(Ed25519PublicKeyTest, DifferentPublicKeyBytesNotEqual) {
+ util::StatusOr<Ed25519Parameters> params =
+ Ed25519Parameters::Create(Ed25519Parameters::Variant::kTink);
+ ASSERT_THAT(params, IsOk());
+
+ std::string public_key_bytes1 = subtle::Random::GetRandomBytes(32);
+ std::string public_key_bytes2 = subtle::Random::GetRandomBytes(32);
+
+ util::StatusOr<Ed25519PublicKey> public_key = Ed25519PublicKey::Create(
+ *params, public_key_bytes1, /*id_requirement=*/0x01020304,
+ GetPartialKeyAccess());
+ ASSERT_THAT(public_key, IsOk());
+
+ util::StatusOr<Ed25519PublicKey> other_public_key = Ed25519PublicKey::Create(
+ *params, public_key_bytes2, /*id_requirement=*/0x01020304,
+ GetPartialKeyAccess());
+ ASSERT_THAT(other_public_key, IsOk());
+
+ EXPECT_TRUE(*public_key != *other_public_key);
+ EXPECT_TRUE(*other_public_key != *public_key);
+ EXPECT_FALSE(*public_key == *other_public_key);
+ EXPECT_FALSE(*other_public_key == *public_key);
+}
+
+TEST(Ed25519PublicKeyTest, DifferentIdRequirementNotEqual) {
+ util::StatusOr<Ed25519Parameters> params =
+ Ed25519Parameters::Create(Ed25519Parameters::Variant::kTink);
+ ASSERT_THAT(params, IsOk());
+
+ std::string public_key_bytes = subtle::Random::GetRandomBytes(32);
+
+ util::StatusOr<Ed25519PublicKey> public_key = Ed25519PublicKey::Create(
+ *params, public_key_bytes, /*id_requirement=*/0x01020304,
+ GetPartialKeyAccess());
+ ASSERT_THAT(public_key, IsOk());
+
+ util::StatusOr<Ed25519PublicKey> other_public_key = Ed25519PublicKey::Create(
+ *params, public_key_bytes, /*id_requirement=*/0x02030405,
+ GetPartialKeyAccess());
+ ASSERT_THAT(other_public_key, IsOk());
+
+ EXPECT_TRUE(*public_key != *other_public_key);
+ EXPECT_TRUE(*other_public_key != *public_key);
+ EXPECT_FALSE(*public_key == *other_public_key);
+ EXPECT_FALSE(*other_public_key == *public_key);
+}
+
+} // namespace
+} // namespace tink
+} // namespace crypto