This repository has been archived by the owner on Apr 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add public key class for SLH-DSA key types.
PiperOrigin-RevId: 624132197
- Loading branch information
1 parent
71e5aaa
commit 52ab085
Showing
3 changed files
with
396 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// Copyright 2024 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/experimental/pqcrypto/signature/slh_dsa_public_key.h" | ||
|
||
#include <string> | ||
|
||
#include "absl/status/status.h" | ||
#include "absl/strings/escaping.h" | ||
#include "absl/strings/str_cat.h" | ||
#include "absl/strings/string_view.h" | ||
#include "absl/types/optional.h" | ||
#include "tink/experimental/pqcrypto/signature/slh_dsa_parameters.h" | ||
#include "tink/key.h" | ||
#include "tink/partial_key_access_token.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 SlhDsaParameters& parameters, absl::optional<int> id_requirement) { | ||
switch (parameters.GetVariant()) { | ||
case SlhDsaParameters::Variant::kNoPrefix: | ||
return std::string(""); // Empty prefix. | ||
case SlhDsaParameters::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<SlhDsaPublicKey> SlhDsaPublicKey::Create( | ||
const SlhDsaParameters& 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"); | ||
} | ||
// Only 32-byte public keys are supported at the moment. | ||
if (public_key_bytes.size() != 32) { | ||
return util::Status(absl::StatusCode::kInvalidArgument, | ||
"Invalid public key size. Only 32-byte keys are " | ||
"currently supported."); | ||
} | ||
util::StatusOr<std::string> output_prefix = | ||
ComputeOutputPrefix(parameters, id_requirement); | ||
if (!output_prefix.ok()) { | ||
return output_prefix.status(); | ||
} | ||
return SlhDsaPublicKey(parameters, public_key_bytes, id_requirement, | ||
*output_prefix); | ||
} | ||
|
||
bool SlhDsaPublicKey::operator==(const Key& other) const { | ||
const SlhDsaPublicKey* that = dynamic_cast<const SlhDsaPublicKey*>(&other); | ||
if (that == nullptr) { | ||
return false; | ||
} | ||
return GetParameters() == that->GetParameters() && | ||
public_key_bytes_ == that->public_key_bytes_ && | ||
id_requirement_ == that->id_requirement_; | ||
} | ||
|
||
} // namespace tink | ||
} // namespace crypto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// Copyright 2024 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_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SLH_DSA_PUBLIC_KEY_H_ | ||
#define TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SLH_DSA_PUBLIC_KEY_H_ | ||
|
||
#include <string> | ||
|
||
#include "absl/base/attributes.h" | ||
#include "absl/strings/string_view.h" | ||
#include "absl/types/optional.h" | ||
#include "tink/experimental/pqcrypto/signature/slh_dsa_parameters.h" | ||
#include "tink/key.h" | ||
#include "tink/partial_key_access_token.h" | ||
#include "tink/signature/signature_public_key.h" | ||
#include "tink/util/statusor.h" | ||
|
||
namespace crypto { | ||
namespace tink { | ||
|
||
// Representation of the verification function for the SLH-DSA digital signature | ||
// primitive. | ||
class SlhDsaPublicKey : public SignaturePublicKey { | ||
public: | ||
// Copyable and movable. | ||
SlhDsaPublicKey(const SlhDsaPublicKey& other) = default; | ||
SlhDsaPublicKey& operator=(const SlhDsaPublicKey& other) = default; | ||
SlhDsaPublicKey(SlhDsaPublicKey&& other) = default; | ||
SlhDsaPublicKey& operator=(SlhDsaPublicKey&& other) = default; | ||
|
||
// Creates a new SLH-DSA public key from `public_key_bytes`. If the | ||
// `parameters` specify a variant that uses a prefix, then `id_requirement` is | ||
// used to compute this prefix. | ||
static util::StatusOr<SlhDsaPublicKey> Create( | ||
const SlhDsaParameters& parameters, absl::string_view public_key_bytes, | ||
absl::optional<int> id_requirement, PartialKeyAccessToken token); | ||
|
||
absl::string_view GetPublicKeyBytes(PartialKeyAccessToken token) const | ||
ABSL_ATTRIBUTE_LIFETIME_BOUND { | ||
return public_key_bytes_; | ||
} | ||
|
||
absl::string_view GetOutputPrefix() const override { return output_prefix_; } | ||
|
||
const SlhDsaParameters& GetParameters() const override { return parameters_; } | ||
|
||
absl::optional<int> GetIdRequirement() const override { | ||
return id_requirement_; | ||
} | ||
|
||
bool operator==(const Key& other) const override; | ||
|
||
private: | ||
explicit SlhDsaPublicKey(const SlhDsaParameters& 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) {} | ||
|
||
SlhDsaParameters parameters_; | ||
std::string public_key_bytes_; | ||
absl::optional<int> id_requirement_; | ||
std::string output_prefix_; | ||
}; | ||
|
||
} // namespace tink | ||
} // namespace crypto | ||
|
||
#endif // TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SLH_DSA_PUBLIC_KEY_H_ |
212 changes: 212 additions & 0 deletions
212
cc/experimental/pqcrypto/signature/slh_dsa_public_key_test.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
// Copyright 2024 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/experimental/pqcrypto/signature/slh_dsa_public_key.h" | ||
|
||
#include <string> | ||
|
||
#include "gmock/gmock.h" | ||
#include "gtest/gtest.h" | ||
#include "absl/status/status.h" | ||
#include "absl/types/optional.h" | ||
#include "tink/experimental/pqcrypto/signature/slh_dsa_parameters.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::HasSubstr; | ||
using ::testing::TestWithParam; | ||
using ::testing::Values; | ||
|
||
struct TestCase { | ||
SlhDsaParameters::Variant variant; | ||
absl::optional<int> id_requirement; | ||
std::string output_prefix; | ||
}; | ||
|
||
using SlhDsaPublicKeyTest = TestWithParam<TestCase>; | ||
|
||
INSTANTIATE_TEST_SUITE_P( | ||
SlhDsaPublicKeyTestSuite, SlhDsaPublicKeyTest, | ||
Values(TestCase{SlhDsaParameters::Variant::kTink, 0x02030400, | ||
std::string("\x01\x02\x03\x04\x00", 5)}, | ||
TestCase{SlhDsaParameters::Variant::kTink, 0x03050709, | ||
std::string("\x01\x03\x05\x07\x09", 5)}, | ||
TestCase{SlhDsaParameters::Variant::kNoPrefix, absl::nullopt, ""})); | ||
|
||
TEST_P(SlhDsaPublicKeyTest, CreatePublicKeyWorks) { | ||
TestCase test_case = GetParam(); | ||
|
||
util::StatusOr<SlhDsaParameters> params = SlhDsaParameters::Create( | ||
SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64, | ||
SlhDsaParameters::SignatureType::kSmallSignature, test_case.variant); | ||
ASSERT_THAT(params, IsOk()); | ||
|
||
std::string public_key_bytes = subtle::Random::GetRandomBytes(32); | ||
util::StatusOr<SlhDsaPublicKey> public_key = | ||
SlhDsaPublicKey::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(SlhDsaPublicKeyTest, CreateWithInvalidPublicKeyLengthFails) { | ||
util::StatusOr<SlhDsaParameters> params = SlhDsaParameters::Create( | ||
SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64, | ||
SlhDsaParameters::SignatureType::kSmallSignature, | ||
SlhDsaParameters::Variant::kTink); | ||
ASSERT_THAT(params, IsOk()); | ||
|
||
std::string public_key_bytes = subtle::Random::GetRandomBytes(31); | ||
|
||
EXPECT_THAT( | ||
SlhDsaPublicKey::Create(*params, public_key_bytes, | ||
/*id_requirement=*/123, GetPartialKeyAccess()) | ||
.status(), | ||
StatusIs(absl::StatusCode::kInvalidArgument, | ||
HasSubstr("Invalid public key size"))); | ||
} | ||
|
||
TEST(SlhDsaPublicKeyTest, CreateKeyWithNoIdRequirementWithTinkParamsFails) { | ||
util::StatusOr<SlhDsaParameters> tink_params = SlhDsaParameters::Create( | ||
SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64, | ||
SlhDsaParameters::SignatureType::kSmallSignature, | ||
SlhDsaParameters::Variant::kTink); | ||
ASSERT_THAT(tink_params, IsOk()); | ||
|
||
std::string public_key_bytes = subtle::Random::GetRandomBytes(32); | ||
|
||
EXPECT_THAT(SlhDsaPublicKey::Create(*tink_params, public_key_bytes, | ||
/*id_requirement=*/absl::nullopt, | ||
GetPartialKeyAccess()) | ||
.status(), | ||
StatusIs(absl::StatusCode::kInvalidArgument, | ||
HasSubstr("key without ID requirement with parameters " | ||
"with ID requirement"))); | ||
} | ||
|
||
TEST(SlhDsaPublicKeyTest, CreateKeyWithIdRequirementWithNoPrefixParamsFails) { | ||
util::StatusOr<SlhDsaParameters> no_prefix_params = | ||
SlhDsaParameters::Create(SlhDsaParameters::HashType::kSha2, | ||
/*private_key_size_in_bytes=*/64, | ||
SlhDsaParameters::SignatureType::kSmallSignature, | ||
SlhDsaParameters::Variant::kNoPrefix); | ||
ASSERT_THAT(no_prefix_params, IsOk()); | ||
|
||
std::string public_key_bytes = subtle::Random::GetRandomBytes(32); | ||
|
||
EXPECT_THAT( | ||
SlhDsaPublicKey::Create(*no_prefix_params, public_key_bytes, | ||
/*id_requirement=*/123, GetPartialKeyAccess()) | ||
.status(), | ||
StatusIs(absl::StatusCode::kInvalidArgument, | ||
HasSubstr("key with ID requirement with parameters without ID " | ||
"requirement"))); | ||
} | ||
|
||
TEST_P(SlhDsaPublicKeyTest, PublicKeyEquals) { | ||
TestCase test_case = GetParam(); | ||
|
||
util::StatusOr<SlhDsaParameters> params = SlhDsaParameters::Create( | ||
SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64, | ||
SlhDsaParameters::SignatureType::kSmallSignature, test_case.variant); | ||
ASSERT_THAT(params, IsOk()); | ||
|
||
std::string public_key_bytes = subtle::Random::GetRandomBytes(32); | ||
|
||
util::StatusOr<SlhDsaPublicKey> public_key = | ||
SlhDsaPublicKey::Create(*params, public_key_bytes, | ||
test_case.id_requirement, GetPartialKeyAccess()); | ||
ASSERT_THAT(public_key, IsOk()); | ||
|
||
util::StatusOr<SlhDsaPublicKey> other_public_key = | ||
SlhDsaPublicKey::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(SlhDsaPublicKeyTest, DifferentPublicKeyBytesNotEqual) { | ||
util::StatusOr<SlhDsaParameters> params = | ||
SlhDsaParameters::Create(SlhDsaParameters::HashType::kSha2, | ||
/*private_key_size_in_bytes=*/64, | ||
SlhDsaParameters::SignatureType::kSmallSignature, | ||
SlhDsaParameters::Variant::kTink); | ||
|
||
std::string public_key_bytes1 = subtle::Random::GetRandomBytes(32); | ||
std::string public_key_bytes2 = subtle::Random::GetRandomBytes(32); | ||
|
||
util::StatusOr<SlhDsaPublicKey> public_key = SlhDsaPublicKey::Create( | ||
*params, public_key_bytes1, /*id_requirement=*/0x01020304, | ||
GetPartialKeyAccess()); | ||
ASSERT_THAT(public_key, IsOk()); | ||
|
||
util::StatusOr<SlhDsaPublicKey> other_public_key = SlhDsaPublicKey::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(SlhDsaPublicKeyTest, DifferentIdRequirementNotEqual) { | ||
util::StatusOr<SlhDsaParameters> params = | ||
SlhDsaParameters::Create(SlhDsaParameters::HashType::kSha2, | ||
/*private_key_size_in_bytes=*/64, | ||
SlhDsaParameters::SignatureType::kSmallSignature, | ||
SlhDsaParameters::Variant::kTink); | ||
|
||
std::string public_key_bytes = subtle::Random::GetRandomBytes(32); | ||
|
||
util::StatusOr<SlhDsaPublicKey> public_key = SlhDsaPublicKey::Create( | ||
*params, public_key_bytes, /*id_requirement=*/0x01020304, | ||
GetPartialKeyAccess()); | ||
ASSERT_THAT(public_key, IsOk()); | ||
|
||
util::StatusOr<SlhDsaPublicKey> other_public_key = SlhDsaPublicKey::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 |