Skip to content
This repository was archived by the owner on Apr 17, 2024. It is now read-only.

Commit 52ab085

Browse files
ioannanedelcucopybara-github
authored andcommittedApr 12, 2024
Add public key class for SLH-DSA key types.
PiperOrigin-RevId: 624132197
1 parent 71e5aaa commit 52ab085

File tree

3 files changed

+396
-0
lines changed

3 files changed

+396
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
////////////////////////////////////////////////////////////////////////////////
16+
17+
#include "tink/experimental/pqcrypto/signature/slh_dsa_public_key.h"
18+
19+
#include <string>
20+
21+
#include "absl/status/status.h"
22+
#include "absl/strings/escaping.h"
23+
#include "absl/strings/str_cat.h"
24+
#include "absl/strings/string_view.h"
25+
#include "absl/types/optional.h"
26+
#include "tink/experimental/pqcrypto/signature/slh_dsa_parameters.h"
27+
#include "tink/key.h"
28+
#include "tink/partial_key_access_token.h"
29+
#include "tink/subtle/subtle_util.h"
30+
#include "tink/util/status.h"
31+
#include "tink/util/statusor.h"
32+
33+
namespace crypto {
34+
namespace tink {
35+
namespace {
36+
37+
util::StatusOr<std::string> ComputeOutputPrefix(
38+
const SlhDsaParameters& parameters, absl::optional<int> id_requirement) {
39+
switch (parameters.GetVariant()) {
40+
case SlhDsaParameters::Variant::kNoPrefix:
41+
return std::string(""); // Empty prefix.
42+
case SlhDsaParameters::Variant::kTink:
43+
if (!id_requirement.has_value()) {
44+
return util::Status(absl::StatusCode::kInvalidArgument,
45+
"ID requirement must have value with kTink");
46+
}
47+
return absl::StrCat(absl::HexStringToBytes("01"),
48+
subtle::BigEndian32(*id_requirement));
49+
default:
50+
return util::Status(
51+
absl::StatusCode::kInvalidArgument,
52+
absl::StrCat("Invalid variant: ", parameters.GetVariant()));
53+
}
54+
}
55+
56+
} // namespace
57+
58+
util::StatusOr<SlhDsaPublicKey> SlhDsaPublicKey::Create(
59+
const SlhDsaParameters& parameters, absl::string_view public_key_bytes,
60+
absl::optional<int> id_requirement, PartialKeyAccessToken token) {
61+
if (parameters.HasIdRequirement() && !id_requirement.has_value()) {
62+
return util::Status(
63+
absl::StatusCode::kInvalidArgument,
64+
"Cannot create key without ID requirement with parameters with ID "
65+
"requirement");
66+
}
67+
if (!parameters.HasIdRequirement() && id_requirement.has_value()) {
68+
return util::Status(
69+
absl::StatusCode::kInvalidArgument,
70+
"Cannot create key with ID requirement with parameters without ID "
71+
"requirement");
72+
}
73+
// Only 32-byte public keys are supported at the moment.
74+
if (public_key_bytes.size() != 32) {
75+
return util::Status(absl::StatusCode::kInvalidArgument,
76+
"Invalid public key size. Only 32-byte keys are "
77+
"currently supported.");
78+
}
79+
util::StatusOr<std::string> output_prefix =
80+
ComputeOutputPrefix(parameters, id_requirement);
81+
if (!output_prefix.ok()) {
82+
return output_prefix.status();
83+
}
84+
return SlhDsaPublicKey(parameters, public_key_bytes, id_requirement,
85+
*output_prefix);
86+
}
87+
88+
bool SlhDsaPublicKey::operator==(const Key& other) const {
89+
const SlhDsaPublicKey* that = dynamic_cast<const SlhDsaPublicKey*>(&other);
90+
if (that == nullptr) {
91+
return false;
92+
}
93+
return GetParameters() == that->GetParameters() &&
94+
public_key_bytes_ == that->public_key_bytes_ &&
95+
id_requirement_ == that->id_requirement_;
96+
}
97+
98+
} // namespace tink
99+
} // namespace crypto
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
////////////////////////////////////////////////////////////////////////////////
16+
17+
#ifndef TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SLH_DSA_PUBLIC_KEY_H_
18+
#define TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SLH_DSA_PUBLIC_KEY_H_
19+
20+
#include <string>
21+
22+
#include "absl/base/attributes.h"
23+
#include "absl/strings/string_view.h"
24+
#include "absl/types/optional.h"
25+
#include "tink/experimental/pqcrypto/signature/slh_dsa_parameters.h"
26+
#include "tink/key.h"
27+
#include "tink/partial_key_access_token.h"
28+
#include "tink/signature/signature_public_key.h"
29+
#include "tink/util/statusor.h"
30+
31+
namespace crypto {
32+
namespace tink {
33+
34+
// Representation of the verification function for the SLH-DSA digital signature
35+
// primitive.
36+
class SlhDsaPublicKey : public SignaturePublicKey {
37+
public:
38+
// Copyable and movable.
39+
SlhDsaPublicKey(const SlhDsaPublicKey& other) = default;
40+
SlhDsaPublicKey& operator=(const SlhDsaPublicKey& other) = default;
41+
SlhDsaPublicKey(SlhDsaPublicKey&& other) = default;
42+
SlhDsaPublicKey& operator=(SlhDsaPublicKey&& other) = default;
43+
44+
// Creates a new SLH-DSA public key from `public_key_bytes`. If the
45+
// `parameters` specify a variant that uses a prefix, then `id_requirement` is
46+
// used to compute this prefix.
47+
static util::StatusOr<SlhDsaPublicKey> Create(
48+
const SlhDsaParameters& parameters, absl::string_view public_key_bytes,
49+
absl::optional<int> id_requirement, PartialKeyAccessToken token);
50+
51+
absl::string_view GetPublicKeyBytes(PartialKeyAccessToken token) const
52+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
53+
return public_key_bytes_;
54+
}
55+
56+
absl::string_view GetOutputPrefix() const override { return output_prefix_; }
57+
58+
const SlhDsaParameters& GetParameters() const override { return parameters_; }
59+
60+
absl::optional<int> GetIdRequirement() const override {
61+
return id_requirement_;
62+
}
63+
64+
bool operator==(const Key& other) const override;
65+
66+
private:
67+
explicit SlhDsaPublicKey(const SlhDsaParameters& parameters,
68+
absl::string_view public_key_bytes,
69+
absl::optional<int> id_requirement,
70+
absl::string_view output_prefix)
71+
: parameters_(parameters),
72+
public_key_bytes_(public_key_bytes),
73+
id_requirement_(id_requirement),
74+
output_prefix_(output_prefix) {}
75+
76+
SlhDsaParameters parameters_;
77+
std::string public_key_bytes_;
78+
absl::optional<int> id_requirement_;
79+
std::string output_prefix_;
80+
};
81+
82+
} // namespace tink
83+
} // namespace crypto
84+
85+
#endif // TINK_EXPERIMENTAL_PQCRYPTO_SIGNATURE_SLH_DSA_PUBLIC_KEY_H_
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
////////////////////////////////////////////////////////////////////////////////
16+
17+
#include "tink/experimental/pqcrypto/signature/slh_dsa_public_key.h"
18+
19+
#include <string>
20+
21+
#include "gmock/gmock.h"
22+
#include "gtest/gtest.h"
23+
#include "absl/status/status.h"
24+
#include "absl/types/optional.h"
25+
#include "tink/experimental/pqcrypto/signature/slh_dsa_parameters.h"
26+
#include "tink/partial_key_access.h"
27+
#include "tink/subtle/random.h"
28+
#include "tink/util/statusor.h"
29+
#include "tink/util/test_matchers.h"
30+
31+
namespace crypto {
32+
namespace tink {
33+
namespace {
34+
35+
using ::crypto::tink::test::IsOk;
36+
using ::crypto::tink::test::StatusIs;
37+
using ::testing::Eq;
38+
using ::testing::HasSubstr;
39+
using ::testing::TestWithParam;
40+
using ::testing::Values;
41+
42+
struct TestCase {
43+
SlhDsaParameters::Variant variant;
44+
absl::optional<int> id_requirement;
45+
std::string output_prefix;
46+
};
47+
48+
using SlhDsaPublicKeyTest = TestWithParam<TestCase>;
49+
50+
INSTANTIATE_TEST_SUITE_P(
51+
SlhDsaPublicKeyTestSuite, SlhDsaPublicKeyTest,
52+
Values(TestCase{SlhDsaParameters::Variant::kTink, 0x02030400,
53+
std::string("\x01\x02\x03\x04\x00", 5)},
54+
TestCase{SlhDsaParameters::Variant::kTink, 0x03050709,
55+
std::string("\x01\x03\x05\x07\x09", 5)},
56+
TestCase{SlhDsaParameters::Variant::kNoPrefix, absl::nullopt, ""}));
57+
58+
TEST_P(SlhDsaPublicKeyTest, CreatePublicKeyWorks) {
59+
TestCase test_case = GetParam();
60+
61+
util::StatusOr<SlhDsaParameters> params = SlhDsaParameters::Create(
62+
SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64,
63+
SlhDsaParameters::SignatureType::kSmallSignature, test_case.variant);
64+
ASSERT_THAT(params, IsOk());
65+
66+
std::string public_key_bytes = subtle::Random::GetRandomBytes(32);
67+
util::StatusOr<SlhDsaPublicKey> public_key =
68+
SlhDsaPublicKey::Create(*params, public_key_bytes,
69+
test_case.id_requirement, GetPartialKeyAccess());
70+
ASSERT_THAT(public_key, IsOk());
71+
72+
EXPECT_THAT(public_key->GetParameters(), Eq(*params));
73+
EXPECT_THAT(public_key->GetIdRequirement(), Eq(test_case.id_requirement));
74+
EXPECT_THAT(public_key->GetOutputPrefix(), Eq(test_case.output_prefix));
75+
EXPECT_THAT(public_key->GetPublicKeyBytes(GetPartialKeyAccess()),
76+
Eq(public_key_bytes));
77+
}
78+
79+
TEST(SlhDsaPublicKeyTest, CreateWithInvalidPublicKeyLengthFails) {
80+
util::StatusOr<SlhDsaParameters> params = SlhDsaParameters::Create(
81+
SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64,
82+
SlhDsaParameters::SignatureType::kSmallSignature,
83+
SlhDsaParameters::Variant::kTink);
84+
ASSERT_THAT(params, IsOk());
85+
86+
std::string public_key_bytes = subtle::Random::GetRandomBytes(31);
87+
88+
EXPECT_THAT(
89+
SlhDsaPublicKey::Create(*params, public_key_bytes,
90+
/*id_requirement=*/123, GetPartialKeyAccess())
91+
.status(),
92+
StatusIs(absl::StatusCode::kInvalidArgument,
93+
HasSubstr("Invalid public key size")));
94+
}
95+
96+
TEST(SlhDsaPublicKeyTest, CreateKeyWithNoIdRequirementWithTinkParamsFails) {
97+
util::StatusOr<SlhDsaParameters> tink_params = SlhDsaParameters::Create(
98+
SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64,
99+
SlhDsaParameters::SignatureType::kSmallSignature,
100+
SlhDsaParameters::Variant::kTink);
101+
ASSERT_THAT(tink_params, IsOk());
102+
103+
std::string public_key_bytes = subtle::Random::GetRandomBytes(32);
104+
105+
EXPECT_THAT(SlhDsaPublicKey::Create(*tink_params, public_key_bytes,
106+
/*id_requirement=*/absl::nullopt,
107+
GetPartialKeyAccess())
108+
.status(),
109+
StatusIs(absl::StatusCode::kInvalidArgument,
110+
HasSubstr("key without ID requirement with parameters "
111+
"with ID requirement")));
112+
}
113+
114+
TEST(SlhDsaPublicKeyTest, CreateKeyWithIdRequirementWithNoPrefixParamsFails) {
115+
util::StatusOr<SlhDsaParameters> no_prefix_params =
116+
SlhDsaParameters::Create(SlhDsaParameters::HashType::kSha2,
117+
/*private_key_size_in_bytes=*/64,
118+
SlhDsaParameters::SignatureType::kSmallSignature,
119+
SlhDsaParameters::Variant::kNoPrefix);
120+
ASSERT_THAT(no_prefix_params, IsOk());
121+
122+
std::string public_key_bytes = subtle::Random::GetRandomBytes(32);
123+
124+
EXPECT_THAT(
125+
SlhDsaPublicKey::Create(*no_prefix_params, public_key_bytes,
126+
/*id_requirement=*/123, GetPartialKeyAccess())
127+
.status(),
128+
StatusIs(absl::StatusCode::kInvalidArgument,
129+
HasSubstr("key with ID requirement with parameters without ID "
130+
"requirement")));
131+
}
132+
133+
TEST_P(SlhDsaPublicKeyTest, PublicKeyEquals) {
134+
TestCase test_case = GetParam();
135+
136+
util::StatusOr<SlhDsaParameters> params = SlhDsaParameters::Create(
137+
SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64,
138+
SlhDsaParameters::SignatureType::kSmallSignature, test_case.variant);
139+
ASSERT_THAT(params, IsOk());
140+
141+
std::string public_key_bytes = subtle::Random::GetRandomBytes(32);
142+
143+
util::StatusOr<SlhDsaPublicKey> public_key =
144+
SlhDsaPublicKey::Create(*params, public_key_bytes,
145+
test_case.id_requirement, GetPartialKeyAccess());
146+
ASSERT_THAT(public_key, IsOk());
147+
148+
util::StatusOr<SlhDsaPublicKey> other_public_key =
149+
SlhDsaPublicKey::Create(*params, public_key_bytes,
150+
test_case.id_requirement, GetPartialKeyAccess());
151+
ASSERT_THAT(other_public_key, IsOk());
152+
153+
EXPECT_TRUE(*public_key == *other_public_key);
154+
EXPECT_TRUE(*other_public_key == *public_key);
155+
EXPECT_FALSE(*public_key != *other_public_key);
156+
EXPECT_FALSE(*other_public_key != *public_key);
157+
}
158+
159+
TEST(SlhDsaPublicKeyTest, DifferentPublicKeyBytesNotEqual) {
160+
util::StatusOr<SlhDsaParameters> params =
161+
SlhDsaParameters::Create(SlhDsaParameters::HashType::kSha2,
162+
/*private_key_size_in_bytes=*/64,
163+
SlhDsaParameters::SignatureType::kSmallSignature,
164+
SlhDsaParameters::Variant::kTink);
165+
166+
std::string public_key_bytes1 = subtle::Random::GetRandomBytes(32);
167+
std::string public_key_bytes2 = subtle::Random::GetRandomBytes(32);
168+
169+
util::StatusOr<SlhDsaPublicKey> public_key = SlhDsaPublicKey::Create(
170+
*params, public_key_bytes1, /*id_requirement=*/0x01020304,
171+
GetPartialKeyAccess());
172+
ASSERT_THAT(public_key, IsOk());
173+
174+
util::StatusOr<SlhDsaPublicKey> other_public_key = SlhDsaPublicKey::Create(
175+
*params, public_key_bytes2, /*id_requirement=*/0x01020304,
176+
GetPartialKeyAccess());
177+
ASSERT_THAT(other_public_key, IsOk());
178+
179+
EXPECT_TRUE(*public_key != *other_public_key);
180+
EXPECT_TRUE(*other_public_key != *public_key);
181+
EXPECT_FALSE(*public_key == *other_public_key);
182+
EXPECT_FALSE(*other_public_key == *public_key);
183+
}
184+
185+
TEST(SlhDsaPublicKeyTest, DifferentIdRequirementNotEqual) {
186+
util::StatusOr<SlhDsaParameters> params =
187+
SlhDsaParameters::Create(SlhDsaParameters::HashType::kSha2,
188+
/*private_key_size_in_bytes=*/64,
189+
SlhDsaParameters::SignatureType::kSmallSignature,
190+
SlhDsaParameters::Variant::kTink);
191+
192+
std::string public_key_bytes = subtle::Random::GetRandomBytes(32);
193+
194+
util::StatusOr<SlhDsaPublicKey> public_key = SlhDsaPublicKey::Create(
195+
*params, public_key_bytes, /*id_requirement=*/0x01020304,
196+
GetPartialKeyAccess());
197+
ASSERT_THAT(public_key, IsOk());
198+
199+
util::StatusOr<SlhDsaPublicKey> other_public_key = SlhDsaPublicKey::Create(
200+
*params, public_key_bytes, /*id_requirement=*/0x02030405,
201+
GetPartialKeyAccess());
202+
ASSERT_THAT(other_public_key, IsOk());
203+
204+
EXPECT_TRUE(*public_key != *other_public_key);
205+
EXPECT_TRUE(*other_public_key != *public_key);
206+
EXPECT_FALSE(*public_key == *other_public_key);
207+
EXPECT_FALSE(*other_public_key == *public_key);
208+
}
209+
210+
} // namespace
211+
} // namespace tink
212+
} // namespace crypto

0 commit comments

Comments
 (0)
This repository has been archived.