xref: /aosp_15_r20/external/tink/cc/subtle/rsa_ssa_pkcs1_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_pkcs1_verify_boringssl.h"
18 
19 #include <iostream>
20 #include <memory>
21 #include <string>
22 #include <utility>
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 "openssl/bn.h"
29 #include "include/rapidjson/document.h"
30 #include "tink/internal/err_util.h"
31 #include "tink/internal/fips_utils.h"
32 #include "tink/internal/rsa_util.h"
33 #include "tink/internal/ssl_unique_ptr.h"
34 #include "tink/public_key_sign.h"
35 #include "tink/public_key_verify.h"
36 #include "tink/subtle/common_enums.h"
37 #include "tink/subtle/wycheproof_util.h"
38 #include "tink/util/status.h"
39 #include "tink/util/statusor.h"
40 #include "tink/util/test_matchers.h"
41 
42 namespace crypto {
43 namespace tink {
44 namespace subtle {
45 namespace {
46 
47 using ::crypto::tink::test::IsOk;
48 using ::crypto::tink::test::StatusIs;
49 
50 class RsaSsaPkcs1VerifyBoringSslTest : public ::testing::Test {};
51 
52 // Test vector from
53 // https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Digital-Signatures
54 struct NistTestVector {
55   std::string n;
56   std::string e;
57   std::string message;
58   std::string signature;
59   HashType sig_hash;
60 };
61 
62 static const NistTestVector nist_test_vector{
63     absl::HexStringToBytes(
64         "c47abacc2a84d56f3614d92fd62ed36ddde459664b9301dcd1d61781cfcc026bcb2399"
65         "bee7e75681a80b7bf500e2d08ceae1c42ec0b707927f2b2fe92ae852087d25f1d260cc"
66         "74905ee5f9b254ed05494a9fe06732c3680992dd6f0dc634568d11542a705f83ae96d2"
67         "a49763d5fbb24398edf3702bc94bc168190166492b8671de874bb9cecb058c6c8344aa"
68         "8c93754d6effcd44a41ed7de0a9dcd9144437f212b18881d042d331a4618a9e630ef9b"
69         "b66305e4fdf8f0391b3b2313fe549f0189ff968b92f33c266a4bc2cffc897d1937eeb9"
70         "e406f5d0eaa7a14782e76af3fce98f54ed237b4a04a4159a5f6250a296a902880204e6"
71         "1d891c4da29f2d65f34cbb"),
72     absl::HexStringToBytes("49d2a1"),
73     absl::HexStringToBytes(
74         "95123c8d1b236540b86976a11cea31f8bd4e6c54c235147d20ce722b03a6ad756fbd91"
75         "8c27df8ea9ce3104444c0bbe877305bc02e35535a02a58dcda306e632ad30b3dc3ce0b"
76         "a97fdf46ec192965dd9cd7f4a71b02b8cba3d442646eeec4af590824ca98d74fbca934"
77         "d0b6867aa1991f3040b707e806de6e66b5934f05509bea"),
78     absl::HexStringToBytes(
79         "51265d96f11ab338762891cb29bf3f1d2b3305107063f5f3245af376dfcc7027d39365"
80         "de70a31db05e9e10eb6148cb7f6425f0c93c4fb0e2291adbd22c77656afc196858a11e"
81         "1c670d9eeb592613e69eb4f3aa501730743ac4464486c7ae68fd509e896f63884e9424"
82         "f69c1c5397959f1e52a368667a598a1fc90125273d9341295d2f8e1cc4969bf228c860"
83         "e07a3546be2eeda1cde48ee94d062801fe666e4a7ae8cb9cd79262c017b081af874ff0"
84         "0453ca43e34efdb43fffb0bb42a4e2d32a5e5cc9e8546a221fe930250e5f5333e0efe5"
85         "8ffebf19369a3b8ae5a67f6a048bc9ef915bda25160729b508667ada84a0c27e7e26cf"
86         "2abca413e5e4693f4a9405"),
87     HashType::SHA256};
88 
TEST_F(RsaSsaPkcs1VerifyBoringSslTest,BasicVerify)89 TEST_F(RsaSsaPkcs1VerifyBoringSslTest, BasicVerify) {
90   if (internal::IsFipsModeEnabled()) {
91     GTEST_SKIP() << "Test not run in FIPS-only mode";
92   }
93 
94   internal::RsaPublicKey pub_key{nist_test_vector.n, nist_test_vector.e};
95   internal::RsaSsaPkcs1Params params{nist_test_vector.sig_hash};
96 
97   auto verifier_result = RsaSsaPkcs1VerifyBoringSsl::New(pub_key, params);
98   ASSERT_TRUE(verifier_result.ok()) << verifier_result.status();
99   auto verifier = std::move(verifier_result.value());
100   auto status =
101       verifier->Verify(nist_test_vector.signature, nist_test_vector.message);
102   EXPECT_TRUE(status.ok()) << status << internal::GetSslErrors();
103 }
104 
TEST_F(RsaSsaPkcs1VerifyBoringSslTest,NewErrors)105 TEST_F(RsaSsaPkcs1VerifyBoringSslTest, NewErrors) {
106   if (internal::IsFipsModeEnabled()) {
107     GTEST_SKIP() << "Test not run in FIPS-only mode";
108   }
109 
110   internal::RsaPublicKey nist_pub_key{nist_test_vector.n, nist_test_vector.e};
111   internal::RsaSsaPkcs1Params nist_params{nist_test_vector.sig_hash};
112   internal::RsaPublicKey small_pub_key{std::string("\x23"), std::string("\x3")};
113   internal::RsaSsaPkcs1Params sha1_hash_params{HashType::SHA1};
114 
115   {  // Small modulus.
116     auto result = RsaSsaPkcs1VerifyBoringSsl::New(small_pub_key, nist_params);
117     EXPECT_FALSE(result.ok());
118     EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
119     EXPECT_PRED_FORMAT2(testing::IsSubstring,
120                         "only modulus size >= 2048-bit is supported",
121                         std::string(result.status().message()));
122   }
123 
124   {  // Use SHA1 for digital signature.
125     auto result =
126         RsaSsaPkcs1VerifyBoringSsl::New(nist_pub_key, sha1_hash_params);
127     EXPECT_FALSE(result.ok());
128     EXPECT_EQ(absl::StatusCode::kInvalidArgument, result.status().code());
129     EXPECT_PRED_FORMAT2(testing::IsSubstring,
130                         "SHA1 is not safe for digital signature",
131                         std::string(result.status().message()));
132   }
133 }
134 
TEST_F(RsaSsaPkcs1VerifyBoringSslTest,Modification)135 TEST_F(RsaSsaPkcs1VerifyBoringSslTest, Modification) {
136   if (internal::IsFipsModeEnabled()) {
137     GTEST_SKIP() << "Test not run in FIPS-only mode";
138   }
139 
140   internal::RsaPublicKey pub_key{nist_test_vector.n, nist_test_vector.e};
141   internal::RsaSsaPkcs1Params params{nist_test_vector.sig_hash};
142 
143   auto verifier_result = RsaSsaPkcs1VerifyBoringSsl::New(pub_key, params);
144   ASSERT_TRUE(verifier_result.ok()) << verifier_result.status();
145   auto verifier = std::move(verifier_result.value());
146   // Modify the message.
147   for (std::size_t i = 0; i < nist_test_vector.message.length(); i++) {
148     std::string modified_message = nist_test_vector.message;
149     modified_message[i / 8] ^= 1 << (i % 8);
150     auto status =
151         verifier->Verify(nist_test_vector.signature, modified_message);
152     EXPECT_FALSE(status.ok()) << status << internal::GetSslErrors();
153   }
154   // Modify the signature.
155   for (std::size_t i = 0; i < nist_test_vector.signature.length(); i++) {
156     std::string modified_signature = nist_test_vector.signature;
157     modified_signature[i / 8] ^= 1 << (i % 8);
158     auto status =
159         verifier->Verify(modified_signature, nist_test_vector.message);
160     EXPECT_FALSE(status.ok()) << status << internal::GetSslErrors();
161   }
162   // Truncate the signature.
163   for (std::size_t i = 0; i < nist_test_vector.signature.length(); i++) {
164     std::string truncated_signature(nist_test_vector.signature, 0, i);
165     auto status =
166         verifier->Verify(truncated_signature, nist_test_vector.message);
167     EXPECT_FALSE(status.ok()) << status << internal::GetSslErrors();
168   }
169 }
170 
GetVerifier(const rapidjson::Value & test_group)171 static util::StatusOr<std::unique_ptr<RsaSsaPkcs1VerifyBoringSsl>> GetVerifier(
172     const rapidjson::Value& test_group) {
173   internal::RsaPublicKey key;
174   key.n = WycheproofUtil::GetInteger(test_group["n"]);
175   key.e = WycheproofUtil::GetInteger(test_group["e"]);
176 
177   HashType md = WycheproofUtil::GetHashType(test_group["sha"]);
178   internal::RsaSsaPkcs1Params params;
179   params.hash_type = md;
180 
181   auto result = RsaSsaPkcs1VerifyBoringSsl::New(key, params);
182   if (!result.ok()) {
183     std::cout << "Failed: " << result.status() << "\n";
184   }
185   return result;
186 }
187 
188 // Tests signature verification using the test vectors in the specified file.
189 // allow_skipping determines whether it is OK to skip a test because
190 // a verfier cannot be constructed. This option can be used for
191 // if a file contains test vectors that are not necessarily supported
192 // by tink.
TestSignatures(const std::string & filename,bool allow_skipping)193 bool TestSignatures(const std::string& filename, bool allow_skipping) {
194   std::unique_ptr<rapidjson::Document> root =
195       WycheproofUtil::ReadTestVectors(filename);
196   std::cout << (*root)["algorithm"].GetString();
197   std::cout << "generator version " << (*root)["generatorVersion"].GetString();
198   std::cout << "expected version 0.4.12";
199   int passed_tests = 0;
200   int failed_tests = 0;
201   int group_count = 0;
202   for (const rapidjson::Value& test_group : (*root)["testGroups"].GetArray()) {
203     group_count++;
204     auto verifier_result = GetVerifier(test_group);
205     if (!verifier_result.ok()) {
206       std::string type = test_group["type"].GetString();
207       if (allow_skipping) {
208         std::cout << "Could not construct verifier for " << type << " group "
209                   << group_count << ": " << verifier_result.status();
210       } else {
211         ADD_FAILURE() << "Could not construct verifier for " << type
212                       << " group " << group_count << ": "
213                       << verifier_result.status();
214         failed_tests += test_group["tests"].GetArray().Size();
215       }
216       continue;
217     }
218     auto verifier = std::move(verifier_result.value());
219     for (const rapidjson::Value& test : test_group["tests"].GetArray()) {
220       std::string expected = test["result"].GetString();
221       std::string msg = WycheproofUtil::GetBytes(test["msg"]);
222       std::string sig = WycheproofUtil::GetBytes(test["sig"]);
223       std::string id =
224           absl::StrCat(test["tcId"].GetInt(), " ", test["comment"].GetString());
225       auto status = verifier->Verify(sig, msg);
226       if (expected == "valid") {
227         if (status.ok()) {
228           ++passed_tests;
229         } else {
230           ++failed_tests;
231           ADD_FAILURE() << "Valid signature not verified:" << id
232                         << " status:" << status;
233         }
234       } else if (expected == "invalid") {
235         if (!status.ok()) {
236           ++passed_tests;
237         } else {
238           ++failed_tests;
239           ADD_FAILURE() << "Invalid signature verified:" << id;
240         }
241       } else if (expected == "acceptable") {
242         // The validity of the signature is undefined. Hence the test passes
243         // but we log the result since we might still want to know if the
244         // library is strict or forgiving.
245         ++passed_tests;
246         std::cout << "Acceptable signature:" << id << ":" << status;
247       } else {
248         ++failed_tests;
249         ADD_FAILURE() << "Invalid field result:" << expected;
250       }
251     }
252   }
253   int num_tests = (*root)["numberOfTests"].GetInt();
254   std::cout << "total number of tests: " << num_tests;
255   std::cout << "number of tests passed:" << passed_tests;
256   std::cout << "number of tests failed:" << failed_tests;
257   return failed_tests == 0;
258 }
259 
TEST_F(RsaSsaPkcs1VerifyBoringSslTest,WycheproofRsaPkcs12048SHA256)260 TEST_F(RsaSsaPkcs1VerifyBoringSslTest, WycheproofRsaPkcs12048SHA256) {
261   if (internal::IsFipsModeEnabled()) {
262     GTEST_SKIP() << "Test not run in FIPS-only mode";
263   }
264   ASSERT_TRUE(TestSignatures("rsa_signature_2048_sha256_test.json",
265                              /*allow_skipping=*/true));
266 }
267 
TEST_F(RsaSsaPkcs1VerifyBoringSslTest,WycheproofRsaPkcs13072SHA256)268 TEST_F(RsaSsaPkcs1VerifyBoringSslTest, WycheproofRsaPkcs13072SHA256) {
269   if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) {
270     GTEST_SKIP()
271         << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable.";
272   }
273   ASSERT_TRUE(TestSignatures("rsa_signature_3072_sha256_test.json",
274                              /*allow_skipping=*/true));
275 }
276 
TEST_F(RsaSsaPkcs1VerifyBoringSslTest,WycheproofRsaPkcs13072SHA512)277 TEST_F(RsaSsaPkcs1VerifyBoringSslTest, WycheproofRsaPkcs13072SHA512) {
278   if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) {
279     GTEST_SKIP()
280         << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable.";
281   }
282   ASSERT_TRUE(TestSignatures("rsa_signature_3072_sha512_test.json",
283                              /*allow_skipping=*/true));
284 }
285 
TEST_F(RsaSsaPkcs1VerifyBoringSslTest,WycheproofRsaPkcs14096SHA512)286 TEST_F(RsaSsaPkcs1VerifyBoringSslTest, WycheproofRsaPkcs14096SHA512) {
287   if (internal::IsFipsModeEnabled()) {
288     GTEST_SKIP() << "Test not run in FIPS-only mode";
289   }
290   ASSERT_TRUE(TestSignatures("rsa_signature_4096_sha512_test.json",
291                              /*allow_skipping=*/true));
292 }
293 
294 // FIPS-only mode test
TEST_F(RsaSsaPkcs1VerifyBoringSslTest,TestFipsFailWithoutBoringCrypto)295 TEST_F(RsaSsaPkcs1VerifyBoringSslTest, TestFipsFailWithoutBoringCrypto) {
296   if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) {
297     GTEST_SKIP()
298         << "Test assumes kOnlyUseFips but BoringCrypto is unavailable.";
299   }
300 
301   internal::RsaPublicKey pub_key{nist_test_vector.n, nist_test_vector.e};
302   internal::RsaSsaPkcs1Params params{/*sig_hash=*/HashType::SHA256};
303   EXPECT_THAT(RsaSsaPkcs1VerifyBoringSsl::New(pub_key, params).status(),
304               StatusIs(absl::StatusCode::kInternal));
305 }
306 
TEST_F(RsaSsaPkcs1VerifyBoringSslTest,TestAllowedFipsModuli)307 TEST_F(RsaSsaPkcs1VerifyBoringSslTest, TestAllowedFipsModuli) {
308   if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) {
309     GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto.";
310   }
311 
312   internal::SslUniquePtr<BIGNUM> rsa_f4(BN_new());
313   internal::RsaPrivateKey private_key;
314   internal::RsaPublicKey public_key;
315   BN_set_word(rsa_f4.get(), RSA_F4);
316 
317   EXPECT_THAT(
318       internal::NewRsaKeyPair(3072, rsa_f4.get(), &private_key, &public_key),
319       IsOk());
320 
321   internal::RsaSsaPkcs1Params params{/*sig_hash=*/HashType::SHA256};
322   EXPECT_THAT(RsaSsaPkcs1VerifyBoringSsl::New(public_key, params).status(),
323               IsOk());
324 }
325 
TEST_F(RsaSsaPkcs1VerifyBoringSslTest,TestRestrictedFipsModuli)326 TEST_F(RsaSsaPkcs1VerifyBoringSslTest, TestRestrictedFipsModuli) {
327   if (!internal::IsFipsModeEnabled() || !internal::IsFipsEnabledInSsl()) {
328     GTEST_SKIP() << "Test assumes kOnlyUseFips and BoringCrypto.";
329   }
330 
331   internal::SslUniquePtr<BIGNUM> rsa_f4(BN_new());
332   internal::RsaPrivateKey private_key;
333   internal::RsaPublicKey public_key;
334   internal::RsaSsaPkcs1Params params{/*sig_hash=*/HashType::SHA256};
335   BN_set_word(rsa_f4.get(), RSA_F4);
336 
337   EXPECT_THAT(
338       internal::NewRsaKeyPair(2560, rsa_f4.get(), &private_key, &public_key),
339       IsOk());
340 
341   EXPECT_THAT(RsaSsaPkcs1VerifyBoringSsl::New(public_key, params).status(),
342               StatusIs(absl::StatusCode::kInternal));
343 
344   EXPECT_THAT(
345       internal::NewRsaKeyPair(4096, rsa_f4.get(), &private_key, &public_key),
346       IsOk());
347 
348   EXPECT_THAT(RsaSsaPkcs1VerifyBoringSsl::New(public_key, params).status(),
349               StatusIs(absl::StatusCode::kInternal));
350 }
351 
352 }  // namespace
353 }  // namespace subtle
354 }  // namespace tink
355 }  // namespace crypto
356