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_verify_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/dilithium_sign_key_manager.h"
27 #include "tink/experimental/pqcrypto/signature/subtle/dilithium_avx2_sign.h"
28 #include "tink/experimental/pqcrypto/signature/subtle/dilithium_avx2_verify.h"
29 #include "tink/experimental/pqcrypto/signature/subtle/dilithium_key.h"
30 #include "tink/experimental/pqcrypto/signature/util/enums.h"
31 #include "tink/public_key_verify.h"
32 #include "tink/util/secret_data.h"
33 #include "tink/util/status.h"
34 #include "tink/util/statusor.h"
35 #include "tink/util/test_matchers.h"
36
37 extern "C" {
38 #include "third_party/pqclean/crypto_sign/dilithium2/api.h"
39 #include "third_party/pqclean/crypto_sign/dilithium2aes/api.h"
40 #include "third_party/pqclean/crypto_sign/dilithium3/api.h"
41 #include "third_party/pqclean/crypto_sign/dilithium3aes/api.h"
42 #include "third_party/pqclean/crypto_sign/dilithium5/api.h"
43 #include "third_party/pqclean/crypto_sign/dilithium5aes/api.h"
44 }
45
46 namespace crypto {
47 namespace tink {
48 namespace {
49
50 using ::crypto::tink::subtle::DilithiumPrivateKeyPqclean;
51 using ::crypto::tink::test::IsOk;
52 using ::crypto::tink::util::EnumsPqcrypto;
53 using ::crypto::tink::util::StatusOr;
54 using ::google::crypto::tink::DilithiumKeyFormat;
55 using ::google::crypto::tink::DilithiumParams;
56 using ::google::crypto::tink::DilithiumPrivateKey;
57 using ::google::crypto::tink::DilithiumPublicKey;
58 using ::google::crypto::tink::DilithiumSeedExpansion;
59 using ::google::crypto::tink::KeyData;
60 using ::testing::Eq;
61 using ::testing::Not;
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 DilithiumVerifyKeyManagerTest = testing::TestWithParam<DilithiumTestCase>;
71
72 // Helper function that returns a valid dilithium private key.
CreateValidPrivateKey(int32_t private_key_size,DilithiumSeedExpansion seed_expansion)73 StatusOr<DilithiumPrivateKey> CreateValidPrivateKey(
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 DilithiumSignKeyManager().CreateKey(key_format);
81 }
82
83 // Helper function that returns a valid dilithium public key.
CreateValidPublicKey(int32_t private_key_size,DilithiumSeedExpansion seed_expansion)84 StatusOr<DilithiumPublicKey> CreateValidPublicKey(
85 int32_t private_key_size, DilithiumSeedExpansion seed_expansion) {
86 StatusOr<DilithiumPrivateKey> private_key =
87 CreateValidPrivateKey(private_key_size, seed_expansion);
88
89 if (!private_key.ok()) return private_key.status();
90 return DilithiumSignKeyManager().GetPublicKey(*private_key);
91 }
92
TEST(DilithiumVerifyKeyManagerTest,Basics)93 TEST(DilithiumVerifyKeyManagerTest, Basics) {
94 EXPECT_THAT(DilithiumVerifyKeyManager().get_version(), Eq(0));
95 EXPECT_THAT(DilithiumVerifyKeyManager().key_material_type(),
96 Eq(KeyData::ASYMMETRIC_PUBLIC));
97 EXPECT_THAT(DilithiumVerifyKeyManager().get_key_type(),
98 Eq("type.googleapis.com/google.crypto.tink.DilithiumPublicKey"));
99 }
100
TEST(DilithiumVerifyKeyManagerTest,ValidateEmptyKey)101 TEST(DilithiumVerifyKeyManagerTest, ValidateEmptyKey) {
102 EXPECT_THAT(DilithiumVerifyKeyManager().ValidateKey(DilithiumPublicKey()),
103 Not(IsOk()));
104 }
105
TEST_P(DilithiumVerifyKeyManagerTest,InvalidParams)106 TEST_P(DilithiumVerifyKeyManagerTest, InvalidParams) {
107 const DilithiumTestCase& test_case = GetParam();
108
109 DilithiumKeyFormat key_format;
110 DilithiumParams* params = key_format.mutable_params();
111 params->set_key_size(test_case.private_key_size);
112 params->set_seed_expansion(DilithiumSeedExpansion::SEED_EXPANSION_UNKNOWN);
113
114 EXPECT_THAT(DilithiumVerifyKeyManager().ValidateParams(*params), Not(IsOk()));
115 }
116
TEST_P(DilithiumVerifyKeyManagerTest,PublicKeyValid)117 TEST_P(DilithiumVerifyKeyManagerTest, PublicKeyValid) {
118 const DilithiumTestCase& test_case = GetParam();
119
120 StatusOr<DilithiumPublicKey> public_key = CreateValidPublicKey(
121 test_case.private_key_size, test_case.seed_expansion);
122 ASSERT_THAT(public_key, IsOk());
123
124 EXPECT_THAT(DilithiumVerifyKeyManager().ValidateKey(*public_key), IsOk());
125 }
126
TEST_P(DilithiumVerifyKeyManagerTest,PublicKeyWrongVersion)127 TEST_P(DilithiumVerifyKeyManagerTest, PublicKeyWrongVersion) {
128 const DilithiumTestCase& test_case = GetParam();
129
130 StatusOr<DilithiumPublicKey> public_key = CreateValidPublicKey(
131 test_case.private_key_size, test_case.seed_expansion);
132 ASSERT_THAT(public_key, IsOk());
133
134 public_key->set_version(1);
135 EXPECT_THAT(DilithiumVerifyKeyManager().ValidateKey(*public_key),
136 Not(IsOk()));
137 }
138
TEST_P(DilithiumVerifyKeyManagerTest,PublicKeyWrongKeyLength)139 TEST_P(DilithiumVerifyKeyManagerTest, PublicKeyWrongKeyLength) {
140 const DilithiumTestCase& test_case = GetParam();
141
142 StatusOr<DilithiumPublicKey> public_key = CreateValidPublicKey(
143 test_case.private_key_size, test_case.seed_expansion);
144 ASSERT_THAT(public_key, IsOk());
145
146 for (int keysize = 0; keysize < PQCLEAN_DILITHIUM2_CRYPTO_PUBLICKEYBYTES;
147 keysize++) {
148 public_key->set_key_value(std::string(keysize, '@'));
149 EXPECT_THAT(DilithiumVerifyKeyManager().ValidateKey(*public_key),
150 Not(IsOk()));
151 }
152 }
153
TEST_P(DilithiumVerifyKeyManagerTest,Create)154 TEST_P(DilithiumVerifyKeyManagerTest, Create) {
155 const DilithiumTestCase& test_case = GetParam();
156
157 StatusOr<DilithiumPrivateKey> private_key = CreateValidPrivateKey(
158 test_case.private_key_size, test_case.seed_expansion);
159 ASSERT_THAT(private_key, IsOk());
160
161 StatusOr<DilithiumPublicKey> public_key =
162 DilithiumSignKeyManager().GetPublicKey(*private_key);
163 ASSERT_THAT(public_key, IsOk());
164
165 util::StatusOr<DilithiumPrivateKeyPqclean> dilithium_private_key =
166 DilithiumPrivateKeyPqclean::NewPrivateKey(
167 util::SecretDataFromStringView(private_key->key_value()),
168 EnumsPqcrypto::ProtoToSubtle(test_case.seed_expansion));
169 ASSERT_THAT(dilithium_private_key, IsOk());
170
171 util::StatusOr<std::unique_ptr<PublicKeySign>> direct_signer =
172 subtle::DilithiumAvx2Sign::New(*dilithium_private_key);
173 ASSERT_THAT(direct_signer, IsOk());
174
175 util::StatusOr<std::unique_ptr<PublicKeyVerify>> verifier =
176 DilithiumVerifyKeyManager().GetPrimitive<PublicKeyVerify>(*public_key);
177 ASSERT_THAT(verifier, IsOk());
178
179 std::string message = "Some message";
180 util::StatusOr<std::string> signature = (*direct_signer)->Sign(message);
181 ASSERT_THAT(signature, IsOk());
182 EXPECT_THAT((*verifier)->Verify(*signature, message), IsOk());
183 }
184
TEST_P(DilithiumVerifyKeyManagerTest,CreateDifferentPublicKey)185 TEST_P(DilithiumVerifyKeyManagerTest, CreateDifferentPublicKey) {
186 const DilithiumTestCase& test_case = GetParam();
187
188 StatusOr<DilithiumPrivateKey> private_key = CreateValidPrivateKey(
189 test_case.private_key_size, test_case.seed_expansion);
190 ASSERT_THAT(private_key, IsOk());
191
192 // Create a new public key derived from a diffferent private key.
193 StatusOr<DilithiumPrivateKey> new_private_key = CreateValidPrivateKey(
194 test_case.private_key_size, test_case.seed_expansion);
195 ASSERT_THAT(new_private_key, IsOk());
196 StatusOr<DilithiumPublicKey> public_key =
197 DilithiumSignKeyManager().GetPublicKey(*new_private_key);
198 ASSERT_THAT(public_key, IsOk());
199
200 util::StatusOr<DilithiumPrivateKeyPqclean> dilithium_private_key =
201 DilithiumPrivateKeyPqclean::NewPrivateKey(
202 util::SecretDataFromStringView(private_key->key_value()),
203 EnumsPqcrypto::ProtoToSubtle(test_case.seed_expansion));
204 ASSERT_THAT(dilithium_private_key, IsOk());
205
206 util::StatusOr<std::unique_ptr<PublicKeySign>> direct_signer =
207 subtle::DilithiumAvx2Sign::New(*dilithium_private_key);
208 ASSERT_THAT(direct_signer, IsOk());
209
210 util::StatusOr<std::unique_ptr<PublicKeyVerify>> verifier =
211 DilithiumVerifyKeyManager().GetPrimitive<PublicKeyVerify>(*public_key);
212 ASSERT_THAT(verifier, IsOk());
213
214 std::string message = "Some message";
215 util::StatusOr<std::string> signature = (*direct_signer)->Sign(message);
216 ASSERT_THAT(signature, IsOk());
217 EXPECT_THAT((*verifier)->Verify(*signature, message), Not(IsOk()));
218 }
219
220 INSTANTIATE_TEST_SUITE_P(
221 DilithiumVerifyKeyManagerTests, DilithiumVerifyKeyManagerTest,
222 testing::ValuesIn<DilithiumTestCase>({
223 {"Dilithium2", PQCLEAN_DILITHIUM2_CRYPTO_SECRETKEYBYTES,
224 PQCLEAN_DILITHIUM2_CRYPTO_PUBLICKEYBYTES,
225 DilithiumSeedExpansion::SEED_EXPANSION_SHAKE},
226 {"Dilithium3", PQCLEAN_DILITHIUM3_CRYPTO_SECRETKEYBYTES,
227 PQCLEAN_DILITHIUM3_CRYPTO_PUBLICKEYBYTES,
228 DilithiumSeedExpansion::SEED_EXPANSION_SHAKE},
229 {"Dilithium5", PQCLEAN_DILITHIUM5_CRYPTO_SECRETKEYBYTES,
230 PQCLEAN_DILITHIUM5_CRYPTO_PUBLICKEYBYTES,
231 DilithiumSeedExpansion::SEED_EXPANSION_SHAKE},
232 {"Dilithium2Aes", PQCLEAN_DILITHIUM2AES_CRYPTO_SECRETKEYBYTES,
233 PQCLEAN_DILITHIUM2AES_CRYPTO_PUBLICKEYBYTES,
234 DilithiumSeedExpansion::SEED_EXPANSION_AES},
235 {"Dilithium3Aes", PQCLEAN_DILITHIUM3AES_CRYPTO_SECRETKEYBYTES,
236 PQCLEAN_DILITHIUM3AES_CRYPTO_PUBLICKEYBYTES,
237 DilithiumSeedExpansion::SEED_EXPANSION_AES},
238 {"Dilithium5Aes", PQCLEAN_DILITHIUM5AES_CRYPTO_SECRETKEYBYTES,
239 PQCLEAN_DILITHIUM5AES_CRYPTO_PUBLICKEYBYTES,
240 DilithiumSeedExpansion::SEED_EXPANSION_AES},
241 }),
242 [](const testing::TestParamInfo<DilithiumVerifyKeyManagerTest::ParamType>&
__anon74641a020202(const testing::TestParamInfo<DilithiumVerifyKeyManagerTest::ParamType>& info) 243 info) { return info.param.test_name; });
244
245 } // namespace
246
247 } // namespace tink
248 } // namespace crypto
249