1 // Copyright 2021 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/dilithium_sign_key_manager.h"
18
19 #include <memory>
20 #include <string>
21
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 #include "absl/container/flat_hash_set.h"
25 #include "absl/strings/str_cat.h"
26 #include "tink/experimental/pqcrypto/signature/subtle/dilithium_avx2_sign.h"
27 #include "tink/experimental/pqcrypto/signature/subtle/dilithium_avx2_verify.h"
28 #include "tink/experimental/pqcrypto/signature/subtle/dilithium_key.h"
29 #include "tink/experimental/pqcrypto/signature/util/enums.h"
30 #include "tink/public_key_verify.h"
31 #include "tink/util/secret_data.h"
32 #include "tink/util/status.h"
33 #include "tink/util/statusor.h"
34 #include "tink/util/test_matchers.h"
35
36 extern "C" {
37 #include "third_party/pqclean/crypto_sign/dilithium2/api.h"
38 #include "third_party/pqclean/crypto_sign/dilithium2aes/api.h"
39 #include "third_party/pqclean/crypto_sign/dilithium3/api.h"
40 #include "third_party/pqclean/crypto_sign/dilithium3aes/api.h"
41 #include "third_party/pqclean/crypto_sign/dilithium5/api.h"
42 #include "third_party/pqclean/crypto_sign/dilithium5aes/api.h"
43 }
44
45 namespace crypto {
46 namespace tink {
47 namespace {
48
49 using ::crypto::tink::subtle::DilithiumPublicKeyPqclean;
50 using ::crypto::tink::test::IsOk;
51 using ::crypto::tink::util::EnumsPqcrypto;
52 using ::crypto::tink::util::StatusOr;
53 using ::google::crypto::tink::DilithiumKeyFormat;
54 using ::google::crypto::tink::DilithiumParams;
55 using ::google::crypto::tink::DilithiumPrivateKey;
56 using ::google::crypto::tink::DilithiumPublicKey;
57 using ::google::crypto::tink::DilithiumSeedExpansion;
58 using ::google::crypto::tink::KeyData;
59 using ::testing::Eq;
60 using ::testing::Not;
61 using ::testing::SizeIs;
62
63 struct DilithiumTestCase {
64 std::string test_name;
65 int32_t private_key_size;
66 int32_t public_key_size;
67 DilithiumSeedExpansion seed_expansion;
68 };
69
70 using DilithiumSignKeyManagerTest = testing::TestWithParam<DilithiumTestCase>;
71
72 // Helper function that returns a valid dilithium key format.
CreateValidKeyFormat(int32_t private_key_size,DilithiumSeedExpansion seed_expansion)73 StatusOr<DilithiumKeyFormat> CreateValidKeyFormat(
74 int32_t private_key_size, DilithiumSeedExpansion seed_expansion) {
75 DilithiumKeyFormat key_format;
76 DilithiumParams* params = key_format.mutable_params();
77 params->set_key_size(private_key_size);
78 params->set_seed_expansion(seed_expansion);
79
80 return key_format;
81 }
82
TEST(DilithiumSignKeyManagerTest,Basic)83 TEST(DilithiumSignKeyManagerTest, Basic) {
84 EXPECT_THAT(DilithiumSignKeyManager().get_version(), Eq(0));
85 EXPECT_THAT(DilithiumSignKeyManager().key_material_type(),
86 Eq(KeyData::ASYMMETRIC_PRIVATE));
87 EXPECT_THAT(DilithiumSignKeyManager().get_key_type(),
88 Eq("type.googleapis.com/google.crypto.tink.DilithiumPrivateKey"));
89 }
90
TEST_P(DilithiumSignKeyManagerTest,ValidateKeyFormat)91 TEST_P(DilithiumSignKeyManagerTest, ValidateKeyFormat) {
92 const DilithiumTestCase& test_case = GetParam();
93
94 StatusOr<DilithiumKeyFormat> key_format = CreateValidKeyFormat(
95 test_case.private_key_size, test_case.seed_expansion);
96 ASSERT_THAT(key_format, IsOk());
97
98 EXPECT_THAT(DilithiumSignKeyManager().ValidateKeyFormat(*key_format), IsOk());
99 }
100
TEST_P(DilithiumSignKeyManagerTest,PrivateKeyWrongVersion)101 TEST_P(DilithiumSignKeyManagerTest, PrivateKeyWrongVersion) {
102 const DilithiumTestCase& test_case = GetParam();
103
104 StatusOr<DilithiumKeyFormat> key_format = CreateValidKeyFormat(
105 test_case.private_key_size, test_case.seed_expansion);
106 ASSERT_THAT(key_format, IsOk());
107
108 StatusOr<DilithiumPrivateKey> private_key =
109 DilithiumSignKeyManager().CreateKey(*key_format);
110 ASSERT_THAT(private_key, IsOk());
111
112 private_key->set_version(1);
113 EXPECT_THAT(DilithiumSignKeyManager().ValidateKey(*private_key), Not(IsOk()));
114 }
115
TEST_P(DilithiumSignKeyManagerTest,CreateKey)116 TEST_P(DilithiumSignKeyManagerTest, CreateKey) {
117 const DilithiumTestCase& test_case = GetParam();
118
119 StatusOr<DilithiumKeyFormat> key_format = CreateValidKeyFormat(
120 test_case.private_key_size, test_case.seed_expansion);
121 ASSERT_THAT(key_format, IsOk());
122
123 StatusOr<DilithiumPrivateKey> private_key =
124 DilithiumSignKeyManager().CreateKey(*key_format);
125 ASSERT_THAT(private_key, IsOk());
126
127 EXPECT_THAT(private_key->version(), Eq(0));
128 EXPECT_THAT(private_key->public_key().version(), Eq(private_key->version()));
129 EXPECT_THAT(private_key->key_value(), SizeIs(test_case.private_key_size));
130 EXPECT_THAT(private_key->public_key().key_value(),
131 SizeIs(test_case.public_key_size));
132 }
133
TEST_P(DilithiumSignKeyManagerTest,CreateKeyValid)134 TEST_P(DilithiumSignKeyManagerTest, CreateKeyValid) {
135 const DilithiumTestCase& test_case = GetParam();
136
137 StatusOr<DilithiumKeyFormat> key_format = CreateValidKeyFormat(
138 test_case.private_key_size, test_case.seed_expansion);
139 ASSERT_THAT(key_format, IsOk());
140
141 StatusOr<DilithiumPrivateKey> private_key =
142 DilithiumSignKeyManager().CreateKey(*key_format);
143 ASSERT_THAT(private_key, IsOk());
144 EXPECT_THAT(DilithiumSignKeyManager().ValidateKey(*private_key), IsOk());
145 }
146
TEST_P(DilithiumSignKeyManagerTest,CreateKeyAlwaysNew)147 TEST_P(DilithiumSignKeyManagerTest, CreateKeyAlwaysNew) {
148 const DilithiumTestCase& test_case = GetParam();
149
150 StatusOr<DilithiumKeyFormat> key_format = CreateValidKeyFormat(
151 test_case.private_key_size, test_case.seed_expansion);
152 ASSERT_THAT(key_format, IsOk());
153
154 absl::flat_hash_set<std::string> keys;
155 int num_tests = 100;
156 for (int i = 0; i < num_tests; ++i) {
157 StatusOr<DilithiumPrivateKey> private_key =
158 DilithiumSignKeyManager().CreateKey(*key_format);
159 ASSERT_THAT(private_key, IsOk());
160 keys.insert(private_key->key_value());
161 }
162 EXPECT_THAT(keys, SizeIs(num_tests));
163 }
164
TEST_P(DilithiumSignKeyManagerTest,GetPublicKey)165 TEST_P(DilithiumSignKeyManagerTest, GetPublicKey) {
166 const DilithiumTestCase& test_case = GetParam();
167
168 StatusOr<DilithiumKeyFormat> key_format = CreateValidKeyFormat(
169 test_case.private_key_size, test_case.seed_expansion);
170 ASSERT_THAT(key_format, IsOk());
171
172 StatusOr<DilithiumPrivateKey> private_key =
173 DilithiumSignKeyManager().CreateKey(*key_format);
174 ASSERT_THAT(private_key, IsOk());
175
176 StatusOr<DilithiumPublicKey> public_key_or =
177 DilithiumSignKeyManager().GetPublicKey(*private_key);
178 ASSERT_THAT(public_key_or, IsOk());
179
180 EXPECT_THAT(public_key_or->version(),
181 Eq(private_key->public_key().version()));
182 EXPECT_THAT(public_key_or->key_value(),
183 Eq(private_key->public_key().key_value()));
184 }
185
TEST_P(DilithiumSignKeyManagerTest,Create)186 TEST_P(DilithiumSignKeyManagerTest, Create) {
187 const DilithiumTestCase& test_case = GetParam();
188
189 StatusOr<DilithiumKeyFormat> key_format = CreateValidKeyFormat(
190 test_case.private_key_size, test_case.seed_expansion);
191 ASSERT_THAT(key_format, IsOk());
192
193 util::StatusOr<DilithiumPrivateKey> private_key =
194 DilithiumSignKeyManager().CreateKey(*key_format);
195 ASSERT_THAT(private_key, IsOk());
196
197 util::StatusOr<std::unique_ptr<PublicKeySign>> signer =
198 DilithiumSignKeyManager().GetPrimitive<PublicKeySign>(*private_key);
199 ASSERT_THAT(signer, IsOk());
200
201 util::StatusOr<DilithiumPublicKeyPqclean> dilithium_public_key =
202 DilithiumPublicKeyPqclean::NewPublicKey(
203 private_key->public_key().key_value(),
204 EnumsPqcrypto::ProtoToSubtle(test_case.seed_expansion));
205
206 util::StatusOr<std::unique_ptr<PublicKeyVerify>> verifier =
207 subtle::DilithiumAvx2Verify::New(*dilithium_public_key);
208 ASSERT_THAT(verifier, IsOk());
209
210 std::string message = "Some message";
211 util::StatusOr<std::string> signature = (*signer)->Sign(message);
212 ASSERT_THAT(signature, IsOk());
213 EXPECT_THAT((*verifier)->Verify(*signature, message), IsOk());
214 }
215
TEST_P(DilithiumSignKeyManagerTest,CreateDifferentKey)216 TEST_P(DilithiumSignKeyManagerTest, CreateDifferentKey) {
217 const DilithiumTestCase& test_case = GetParam();
218
219 StatusOr<DilithiumKeyFormat> key_format = CreateValidKeyFormat(
220 test_case.private_key_size, test_case.seed_expansion);
221 ASSERT_THAT(key_format, IsOk());
222
223 util::StatusOr<DilithiumPrivateKey> private_key =
224 DilithiumSignKeyManager().CreateKey(*key_format);
225 ASSERT_THAT(private_key, IsOk());
226
227 util::StatusOr<std::unique_ptr<PublicKeySign>> signer =
228 DilithiumSignKeyManager().GetPrimitive<PublicKeySign>(*private_key);
229 ASSERT_THAT(signer, IsOk());
230
231 std::string bad_public_key_data(test_case.public_key_size, '@');
232 util::StatusOr<DilithiumPublicKeyPqclean> dilithium_public_key =
233 DilithiumPublicKeyPqclean::NewPublicKey(
234 bad_public_key_data,
235 EnumsPqcrypto::ProtoToSubtle(test_case.seed_expansion));
236 util::StatusOr<std::unique_ptr<PublicKeyVerify>> verifier =
237 subtle::DilithiumAvx2Verify::New(*dilithium_public_key);
238 ASSERT_THAT(verifier, IsOk());
239
240 std::string message = "Some message";
241 util::StatusOr<std::string> signature = (*signer)->Sign(message);
242 ASSERT_THAT(signature, IsOk());
243 EXPECT_THAT((*verifier)->Verify(*signature, message), Not(IsOk()));
244 }
245
246 INSTANTIATE_TEST_SUITE_P(
247 DilithiumSignKeyManagerTests, DilithiumSignKeyManagerTest,
248 testing::ValuesIn<DilithiumTestCase>({
249 {"Dilithium2", PQCLEAN_DILITHIUM2_CRYPTO_SECRETKEYBYTES,
250 PQCLEAN_DILITHIUM2_CRYPTO_PUBLICKEYBYTES,
251 DilithiumSeedExpansion::SEED_EXPANSION_SHAKE},
252 {"Dilithium3", PQCLEAN_DILITHIUM3_CRYPTO_SECRETKEYBYTES,
253 PQCLEAN_DILITHIUM3_CRYPTO_PUBLICKEYBYTES,
254 DilithiumSeedExpansion::SEED_EXPANSION_SHAKE},
255 {"Dilithium5", PQCLEAN_DILITHIUM5_CRYPTO_SECRETKEYBYTES,
256 PQCLEAN_DILITHIUM5_CRYPTO_PUBLICKEYBYTES,
257 DilithiumSeedExpansion::SEED_EXPANSION_SHAKE},
258 {"Dilithium2Aes", PQCLEAN_DILITHIUM2AES_CRYPTO_SECRETKEYBYTES,
259 PQCLEAN_DILITHIUM2AES_CRYPTO_PUBLICKEYBYTES,
260 DilithiumSeedExpansion::SEED_EXPANSION_AES},
261 {"Dilithium3Aes", PQCLEAN_DILITHIUM3AES_CRYPTO_SECRETKEYBYTES,
262 PQCLEAN_DILITHIUM3AES_CRYPTO_PUBLICKEYBYTES,
263 DilithiumSeedExpansion::SEED_EXPANSION_AES},
264 {"Dilithium5Aes", PQCLEAN_DILITHIUM5AES_CRYPTO_SECRETKEYBYTES,
265 PQCLEAN_DILITHIUM5AES_CRYPTO_PUBLICKEYBYTES,
266 DilithiumSeedExpansion::SEED_EXPANSION_AES},
267 }),
268 [](const testing::TestParamInfo<DilithiumSignKeyManagerTest::ParamType>&
__anon4e7a377c0202(const testing::TestParamInfo<DilithiumSignKeyManagerTest::ParamType>& info) 269 info) { return info.param.test_name; });
270
271 } // namespace
272
273 } // namespace tink
274 } // namespace crypto
275