xref: /aosp_15_r20/external/tink/cc/subtle/rsa_ssa_pss_verify_boringssl_test.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2018 Google Inc.
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/subtle/rsa_ssa_pss_verify_boringssl.h"
18 
19 #include <memory>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include "gtest/gtest.h"
25 #include "absl/status/status.h"
26 #include "absl/strings/escaping.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_split.h"
29 #include "absl/strings/string_view.h"
30 #include "openssl/bn.h"
31 #include "include/rapidjson/document.h"
32 #include "tink/internal/err_util.h"
33 #include "tink/internal/fips_utils.h"
34 #include "tink/internal/rsa_util.h"
35 #include "tink/internal/ssl_unique_ptr.h"
36 #include "tink/public_key_sign.h"
37 #include "tink/public_key_verify.h"
38 #include "tink/subtle/common_enums.h"
39 #include "tink/subtle/wycheproof_util.h"
40 #include "tink/util/status.h"
41 #include "tink/util/statusor.h"
42 #include "tink/util/test_matchers.h"
43 
44 // TODO(quannguyen):
45 //  + Add tests for parameters validation.
46 namespace crypto {
47 namespace tink {
48 namespace subtle {
49 namespace {
50 
51 using ::crypto::tink::test::IsOk;
52 using ::crypto::tink::test::StatusIs;
53 using ::testing::TestParamInfo;
54 using ::testing::ValuesIn;
55 
56 // Test vector from
57 // https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Digital-Signatures
58 struct NistTestVector {
59   std::string n;
60   std::string e;
61   std::string message;
62   std::string signature;
63   HashType sig_hash;
64   HashType mgf1_hash;
65   int salt_length;
66 };
67 
GetNistTestVector()68 NistTestVector GetNistTestVector() {
69   NistTestVector test_vector = {
70       absl::HexStringToBytes(
71           "a47d04e7cacdba4ea26eca8a4c6e14563c2ce03b623b768c0d49868a57121301dbf7"
72           "83d82f4c055e73960e70550187d0af62ac3496f0a3d9103c2eb7919a72752fa7ce8c"
73           "688d81e3aee99468887a15288afbb7acb845b7c522b5c64e678fcd3d22feb84b4427"
74           "2700be527d2b2025a3f83c2383bf6a39cf5b4e48b3cf2f56eef0dfff18555e31037b"
75           "915248694876f3047814415164f2c660881e694b58c28038a032ad25634aad7b3917"
76           "1dee368e3d59bfb7299e4601d4587e68caaf8db457b75af42fc0cf1ae7caced286d7"
77           "7fac6cedb03ad94f1433d2c94d08e60bc1fdef0543cd2951e765b38230fdd18de5d2"
78           "ca627ddc032fe05bbd2ff21e2db1c2f94d8b"),
79       absl::HexStringToBytes("10e43f"),
80       absl::HexStringToBytes(
81           "e002377affb04f0fe4598de9d92d31d6c786040d5776976556a2cfc55e54a1dcb3cb"
82           "1b126bd6a4bed2a184990ccea773fcc79d246553e6c64f686d21ad4152673cafec22"
83           "aeb40f6a084e8a5b4991f4c64cf8a927effd0fd775e71e8329e41fdd4457b3911173"
84           "187b4f09a817d79ea2397fc12dfe3d9c9a0290c8ead31b6690a6"),
85       absl::HexStringToBytes(
86           "4f9b425c2058460e4ab2f5c96384da2327fd29150f01955a76b4efe956af06dc0877"
87           "9a374ee4607eab61a93adc5608f4ec36e47f2a0f754e8ff839a8a19b1db1e884ea4c"
88           "f348cd455069eb87afd53645b44e28a0a56808f5031da5ba9112768dfbfca44ebe63"
89           "a0c0572b731d66122fb71609be1480faa4e4f75e43955159d70f081e2a32fbb19a48"
90           "b9f162cf6b2fb445d2d6994bc58910a26b5943477803cdaaa1bd74b0da0a5d053d8b"
91           "1dc593091db5388383c26079f344e2aea600d0e324164b450f7b9b465111b7265f3b"
92           "1b063089ae7e2623fc0fda8052cf4bf3379102fbf71d7c98e8258664ceed637d20f9"
93           "5ff0111881e650ce61f251d9c3a629ef222d"),
94       HashType::SHA256,
95       HashType::SHA256,
96       32,
97   };
98   return test_vector;
99 }
100 
TEST(RsaSsaPssVerifyBoringSslTest,BasicVerify)101 TEST(RsaSsaPssVerifyBoringSslTest, BasicVerify) {
102   if (internal::IsFipsModeEnabled()) {
103     GTEST_SKIP() << "Test not run in FIPS-only mode";
104   }
105   const NistTestVector kNistTestVector = GetNistTestVector();
106   internal::RsaPublicKey pub_key{kNistTestVector.n, kNistTestVector.e};
107   internal::RsaSsaPssParams params = {
108       kNistTestVector.sig_hash,
109       kNistTestVector.mgf1_hash,
110       kNistTestVector.salt_length,
111   };
112 
113   util::StatusOr<std::unique_ptr<RsaSsaPssVerifyBoringSsl>> verifier =
114       RsaSsaPssVerifyBoringSsl::New(pub_key, params);
115   ASSERT_THAT(verifier, IsOk());
116   util::Status status =
117       (*verifier)->Verify(kNistTestVector.signature, kNistTestVector.message);
118   EXPECT_TRUE(status.ok()) << status << internal::GetSslErrors();
119 }
120 
TEST(RsaSsaPssVerifyBoringSslTest,NewErrors)121 TEST(RsaSsaPssVerifyBoringSslTest, NewErrors) {
122   if (internal::IsFipsModeEnabled()) {
123     GTEST_SKIP() << "Test not run in FIPS-only mode";
124   }
125   const NistTestVector kNistTestVector = GetNistTestVector();
126   internal::RsaPublicKey nist_pub_key{kNistTestVector.n, kNistTestVector.e};
127   internal::RsaSsaPssParams nist_params = {
128       kNistTestVector.sig_hash,
129       kNistTestVector.mgf1_hash,
130       kNistTestVector.salt_length,
131   };
132   internal::RsaPublicKey small_pub_key{std::string("\x23"), std::string("\x3")};
133   internal::RsaSsaPssParams sha1_hash_params = {
134       HashType::SHA1, kNistTestVector.mgf1_hash, kNistTestVector.salt_length};
135 
136   {  // Small modulus.
137     auto result = RsaSsaPssVerifyBoringSsl::New(small_pub_key, nist_params);
138     EXPECT_FALSE(result.ok());
139     EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
140     EXPECT_PRED_FORMAT2(testing::IsSubstring,
141                         "only modulus size >= 2048-bit is supported",
142                         std::string(result.status().message()));
143   }
144 
145   {  // Use SHA1 for digital signature.
146     auto result = RsaSsaPssVerifyBoringSsl::New(nist_pub_key, sha1_hash_params);
147     EXPECT_FALSE(result.ok());
148     EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
149     EXPECT_PRED_FORMAT2(testing::IsSubstring,
150                         "SHA1 is not safe for digital signature",
151                         std::string(result.status().message()));
152   }
153 }
154 
TEST(RsaSsaPssVerifyBoringSslTest,Modification)155 TEST(RsaSsaPssVerifyBoringSslTest, Modification) {
156   if (internal::IsFipsModeEnabled()) {
157     GTEST_SKIP() << "Test not run in FIPS-only mode";
158   }
159   const NistTestVector kNistTestVector = GetNistTestVector();
160   internal::RsaPublicKey pub_key{kNistTestVector.n, kNistTestVector.e};
161   internal::RsaSsaPssParams params = {
162       kNistTestVector.sig_hash,
163       kNistTestVector.mgf1_hash,
164       kNistTestVector.salt_length,
165   };
166 
167   util::StatusOr<std::unique_ptr<RsaSsaPssVerifyBoringSsl>> verifier =
168       RsaSsaPssVerifyBoringSsl::New(pub_key, params);
169   ASSERT_THAT(verifier, IsOk());
170   // Modify the message.
171   for (std::size_t i = 0; i < kNistTestVector.message.length(); i++) {
172     std::string modified_message = kNistTestVector.message;
173     modified_message[i / 8] ^= 1 << (i % 8);
174     util::Status status =
175         (*verifier)->Verify(kNistTestVector.signature, modified_message);
176     EXPECT_FALSE(status.ok()) << status << internal::GetSslErrors();
177   }
178   // Modify the signature.
179   for (std::size_t i = 0; i < kNistTestVector.signature.length(); i++) {
180     std::string modified_signature = kNistTestVector.signature;
181     modified_signature[i / 8] ^= 1 << (i % 8);
182     util::Status status =
183         (*verifier)->Verify(modified_signature, kNistTestVector.message);
184     EXPECT_FALSE(status.ok()) << status << internal::GetSslErrors();
185   }
186   // Truncate the signature.
187   for (std::size_t i = 0; i < kNistTestVector.signature.length(); i++) {
188     std::string truncated_signature(kNistTestVector.signature, 0, i);
189     util::Status status =
190         (*verifier)->Verify(truncated_signature, kNistTestVector.message);
191     EXPECT_FALSE(status.ok()) << status << internal::GetSslErrors();
192   }
193 }
194 
195 // Wycheproof test vector for RSA-SSA PSS.
196 struct RsaSsaPssWycheproofTestVector {
197   std::string file_name;
198   internal::RsaPublicKey key;
199   HashType hash_type;
200   HashType mgf_hash_type;
201   int salt_length;
202   std::string expected;
203   std::string msg;
204   std::string sig;
205   std::string id;
206   std::string comment;
207 };
208 
209 // Reads the RSA-SSA PSS wycheproof test vectors from a given `file_name` and
210 // returns a vector of RsaSsaPssWycheproofTestVector.
ReadTestVectors(absl::string_view file_name)211 std::vector<RsaSsaPssWycheproofTestVector> ReadTestVectors(
212     absl::string_view file_name) {
213   std::vector<RsaSsaPssWycheproofTestVector> test_vectors;
214   std::unique_ptr<rapidjson::Document> root =
215       WycheproofUtil::ReadTestVectors(std::string(file_name));
216   for (const rapidjson::Value& test_group : (*root)["testGroups"].GetArray()) {
217     for (const rapidjson::Value& test : test_group["tests"].GetArray()) {
218       test_vectors.push_back({
219           /*file_name=*/std::string(file_name),
220           /*key=*/
221           {
222               WycheproofUtil::GetInteger(test_group["n"]),
223               WycheproofUtil::GetInteger(test_group["e"]),
224           },
225           /*hash_type=*/WycheproofUtil::GetHashType(test_group["sha"]),
226           /*mgf_hash_type=*/WycheproofUtil::GetHashType(test_group["mgfSha"]),
227           /*salt_length=*/test_group["sLen"].GetInt(),
228           /*expected=*/test["result"].GetString(),
229           /*msg=*/WycheproofUtil::GetBytes(test["msg"]),
230           /*sig=*/WycheproofUtil::GetBytes(test["sig"]),
231           /*id=*/absl::StrCat(test["tcId"].GetInt()),
232           /*comment=*/test["comment"].GetString(),
233       });
234     }
235   }
236   return test_vectors;
237 }
238 
239 // Creates a verifier using the parameters in `test_vector`.
GetVerifier(const RsaSsaPssWycheproofTestVector & test_vector)240 util::StatusOr<std::unique_ptr<RsaSsaPssVerifyBoringSsl>> GetVerifier(
241     const RsaSsaPssWycheproofTestVector& test_vector) {
242   internal::RsaPublicKey key = test_vector.key;
243   internal::RsaSsaPssParams params = {
244       test_vector.hash_type,
245       test_vector.mgf_hash_type,
246       test_vector.salt_length,
247   };
248   return RsaSsaPssVerifyBoringSsl::New(key, params);
249 }
250 
251 using RsaSsaPssWycheproofTest =
252     testing::TestWithParam<RsaSsaPssWycheproofTestVector>;
253 
254 // Tests signature verification using a test vector.
TEST_P(RsaSsaPssWycheproofTest,SignatureVerify)255 TEST_P(RsaSsaPssWycheproofTest, SignatureVerify) {
256   if (internal::IsFipsModeEnabled()) {
257     GTEST_SKIP() << "Test not run in FIPS-only mode";
258   }
259   RsaSsaPssWycheproofTestVector params = GetParam();
260   util::StatusOr<std::unique_ptr<RsaSsaPssVerifyBoringSsl>> verifier =
261       GetVerifier(params);
262   ASSERT_THAT(verifier, IsOk());
263   util::Status result = (*verifier)->Verify(params.sig, params.msg);
264 
265   if (params.expected == "valid") {
266     EXPECT_THAT(result, IsOk());
267   } else if (params.expected == "acceptable") {
268     // The validity of the signature is undefined. Hence we skip the test but we
269     // log the result since we might still want to know if the library is strict
270     // or forgiving.
271     GTEST_SKIP() << "Verification of an acceptable signature " << params.id
272                  << " resulted in: " << result;
273   } else {
274     EXPECT_THAT(result, testing::Not(IsOk()));
275   }
276 }
277 
GetTestParameters()278 std::vector<RsaSsaPssWycheproofTestVector> GetTestParameters() {
279   std::vector<RsaSsaPssWycheproofTestVector> test_vectors = ReadTestVectors(
280       /*file_name=*/"rsa_pss_2048_sha256_mgf1_0_test.json");
281   std::vector<RsaSsaPssWycheproofTestVector> others = ReadTestVectors(
282       /*file_name=*/"rsa_pss_2048_sha256_mgf1_32_test.json");
283   test_vectors.insert(test_vectors.end(), others.begin(), others.end());
284   others = ReadTestVectors(
285       /*file_name=*/"rsa_pss_3072_sha256_mgf1_32_test.json");
286   test_vectors.insert(test_vectors.end(), others.begin(), others.end());
287   others = ReadTestVectors(
288       /*file_name=*/"rsa_pss_4096_sha256_mgf1_32_test.json");
289   test_vectors.insert(test_vectors.end(), others.begin(), others.end());
290   others = ReadTestVectors(
291       /*file_name=*/"rsa_pss_4096_sha512_mgf1_32_test.json");
292   test_vectors.insert(test_vectors.end(), others.begin(), others.end());
293   return test_vectors;
294 }
295 
296 INSTANTIATE_TEST_SUITE_P(
297     RsaSsaPssWycheproofTests, RsaSsaPssWycheproofTest,
298     ValuesIn(GetTestParameters()),
__anond99751590202(const TestParamInfo<RsaSsaPssWycheproofTest::ParamType>& info) 299     [](const TestParamInfo<RsaSsaPssWycheproofTest::ParamType>& info) {
300       // Testcase name is partly using the test name.
301       std::vector<std::string> parts =
302           absl::StrSplit(info.param.file_name, ".");
303       return absl::StrCat(parts[0], "_tid", info.param.id);
304     });
305 
306 // FIPS-only mode test
TEST(RsaSsaPssVerifyBoringSslTest,TestFipsFailWithoutBoringCrypto)307 TEST(RsaSsaPssVerifyBoringSslTest, TestFipsFailWithoutBoringCrypto) {
308   if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) {
309     GTEST_SKIP()
310         << "Test assumes kOnlyUseFips but BoringCrypto is unavailable.";
311   }
312   const NistTestVector kNistTestVector = GetNistTestVector();
313   internal::RsaPublicKey pub_key{kNistTestVector.n, kNistTestVector.e};
314   internal::RsaSsaPssParams params = {
315       /*sig_hash=*/HashType::SHA256,
316       /*mgf1_hash=*/HashType::SHA256,
317       /*salt_length=*/32,
318   };
319   EXPECT_THAT(RsaSsaPssVerifyBoringSsl::New(pub_key, params).status(),
320               StatusIs(absl::StatusCode::kInternal));
321 }
322 
TEST(RsaSsaPssVerifyBoringSslTest,TestAllowedFipsModuli)323 TEST(RsaSsaPssVerifyBoringSslTest, TestAllowedFipsModuli) {
324   if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) {
325     GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto.";
326   }
327 
328   internal::SslUniquePtr<BIGNUM> rsa_f4(BN_new());
329   internal::RsaPrivateKey private_key;
330   internal::RsaPublicKey public_key;
331   internal::RsaSsaPssParams params = {
332       /*sig_hash=*/HashType::SHA256,
333       /*mgf1_hash=*/HashType::SHA256,
334       /*salt_length=*/32,
335   };
336   BN_set_word(rsa_f4.get(), RSA_F4);
337   ASSERT_THAT(
338       internal::NewRsaKeyPair(3072, rsa_f4.get(), &private_key, &public_key),
339       IsOk());
340   EXPECT_THAT(RsaSsaPssVerifyBoringSsl::New(public_key, params).status(),
341               IsOk());
342 }
343 
TEST(RsaSsaPssVerifyBoringSslTest,TestRestrictedFipsModuli)344 TEST(RsaSsaPssVerifyBoringSslTest, TestRestrictedFipsModuli) {
345   if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) {
346     GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto.";
347   }
348 
349   internal::SslUniquePtr<BIGNUM> rsa_f4(BN_new());
350   internal::RsaPrivateKey private_key;
351   internal::RsaPublicKey public_key;
352   internal::RsaSsaPssParams params = {
353       /*sig_hash=*/HashType::SHA256,
354       /*mgf1_hash=*/HashType::SHA256,
355       /*salt_length=*/32,
356   };
357   BN_set_word(rsa_f4.get(), RSA_F4);
358   ASSERT_THAT(
359       internal::NewRsaKeyPair(2560, rsa_f4.get(), &private_key, &public_key),
360       IsOk());
361   EXPECT_THAT(RsaSsaPssVerifyBoringSsl::New(public_key, params).status(),
362               StatusIs(absl::StatusCode::kInternal));
363 
364   ASSERT_THAT(
365       internal::NewRsaKeyPair(4096, rsa_f4.get(), &private_key, &public_key),
366       IsOk());
367   EXPECT_THAT(RsaSsaPssVerifyBoringSsl::New(public_key, params).status(),
368               StatusIs(absl::StatusCode::kInternal));
369 }
370 
371 }  // namespace
372 }  // namespace subtle
373 }  // namespace tink
374 }  // namespace crypto
375