1 // Copyright 2017 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/ecdsa_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/str_cat.h"
27 #include "include/rapidjson/document.h"
28 #include "tink/internal/fips_utils.h"
29 #include "tink/public_key_sign.h"
30 #include "tink/public_key_verify.h"
31 #include "tink/subtle/common_enums.h"
32 #include "tink/subtle/ecdsa_sign_boringssl.h"
33 #include "tink/subtle/subtle_util_boringssl.h"
34 #include "tink/subtle/wycheproof_util.h"
35 #include "tink/util/status.h"
36 #include "tink/util/statusor.h"
37 #include "tink/util/test_matchers.h"
38 #include "tink/util/test_util.h"
39
40 namespace crypto {
41 namespace tink {
42 namespace subtle {
43 namespace {
44
45 using ::crypto::tink::test::StatusIs;
46
47 class EcdsaVerifyBoringSslTest : public ::testing::Test {};
48
TEST_F(EcdsaVerifyBoringSslTest,BasicSigning)49 TEST_F(EcdsaVerifyBoringSslTest, BasicSigning) {
50 if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) {
51 GTEST_SKIP()
52 << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable.";
53 }
54 subtle::EcdsaSignatureEncoding encodings[2] = {
55 EcdsaSignatureEncoding::DER, EcdsaSignatureEncoding::IEEE_P1363};
56 for (EcdsaSignatureEncoding encoding : encodings) {
57 auto ec_key_result =
58 SubtleUtilBoringSSL::GetNewEcKey(EllipticCurveType::NIST_P256);
59 ASSERT_TRUE(ec_key_result.ok()) << ec_key_result.status();
60 auto ec_key = std::move(ec_key_result.value());
61
62 auto signer_result =
63 EcdsaSignBoringSsl::New(ec_key, HashType::SHA256, encoding);
64 ASSERT_TRUE(signer_result.ok()) << signer_result.status();
65 auto signer = std::move(signer_result.value());
66
67 auto verifier_result =
68 EcdsaVerifyBoringSsl::New(ec_key, HashType::SHA256, encoding);
69 ASSERT_TRUE(verifier_result.ok()) << verifier_result.status();
70 auto verifier = std::move(verifier_result.value());
71
72 std::string message = "some data to be signed";
73 auto sign_result = signer->Sign(message);
74 ASSERT_TRUE(sign_result.ok()) << sign_result.status();
75 std::string signature = sign_result.value();
76 EXPECT_NE(signature, message);
77 auto status = verifier->Verify(signature, message);
78 EXPECT_TRUE(status.ok()) << status;
79
80 status = verifier->Verify(signature + "some trailing data", message);
81 EXPECT_FALSE(status.ok()) << status;
82
83 status = verifier->Verify("some bad signature", message);
84 EXPECT_FALSE(status.ok());
85
86 status = verifier->Verify(signature, "some bad message");
87 EXPECT_FALSE(status.ok());
88 }
89 }
90
TEST_F(EcdsaVerifyBoringSslTest,EncodingsMismatch)91 TEST_F(EcdsaVerifyBoringSslTest, EncodingsMismatch) {
92 if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) {
93 GTEST_SKIP()
94 << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable.";
95 }
96 subtle::EcdsaSignatureEncoding encodings[2] = {
97 EcdsaSignatureEncoding::DER, EcdsaSignatureEncoding::IEEE_P1363};
98 for (EcdsaSignatureEncoding encoding : encodings) {
99 auto ec_key_result =
100 SubtleUtilBoringSSL::GetNewEcKey(EllipticCurveType::NIST_P256);
101 ASSERT_TRUE(ec_key_result.ok()) << ec_key_result.status();
102 auto ec_key = std::move(ec_key_result.value());
103
104 auto signer_result =
105 EcdsaSignBoringSsl::New(ec_key, HashType::SHA256, encoding);
106 ASSERT_TRUE(signer_result.ok()) << signer_result.status();
107 auto signer = std::move(signer_result.value());
108
109 auto verifier_result =
110 EcdsaVerifyBoringSsl::New(ec_key, HashType::SHA256,
111 encoding == EcdsaSignatureEncoding::DER
112 ? EcdsaSignatureEncoding::IEEE_P1363
113 : EcdsaSignatureEncoding::DER);
114 ASSERT_TRUE(verifier_result.ok()) << verifier_result.status();
115 auto verifier = std::move(verifier_result.value());
116
117 std::string message = "some data to be signed";
118 auto sign_result = signer->Sign(message);
119 ASSERT_TRUE(sign_result.ok()) << sign_result.status();
120 std::string signature = sign_result.value();
121 EXPECT_NE(signature, message);
122 auto status = verifier->Verify(signature, message);
123 EXPECT_FALSE(status.ok()) << status;
124 }
125 }
126
TEST_F(EcdsaVerifyBoringSslTest,NewErrors)127 TEST_F(EcdsaVerifyBoringSslTest, NewErrors) {
128 if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) {
129 GTEST_SKIP()
130 << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable.";
131 }
132 auto ec_key =
133 SubtleUtilBoringSSL::GetNewEcKey(EllipticCurveType::NIST_P256).value();
134 auto verifier_result = EcdsaVerifyBoringSsl::New(
135 ec_key, HashType::SHA1, EcdsaSignatureEncoding::IEEE_P1363);
136 EXPECT_FALSE(verifier_result.ok()) << verifier_result.status();
137 }
138
GetVerifier(const rapidjson::Value & test_group,subtle::EcdsaSignatureEncoding encoding)139 static util::StatusOr<std::unique_ptr<EcdsaVerifyBoringSsl>> GetVerifier(
140 const rapidjson::Value& test_group,
141 subtle::EcdsaSignatureEncoding encoding) {
142 SubtleUtilBoringSSL::EcKey key;
143 key.pub_x = WycheproofUtil::GetInteger(test_group["key"]["wx"]);
144 key.pub_y = WycheproofUtil::GetInteger(test_group["key"]["wy"]);
145 key.curve = WycheproofUtil::GetEllipticCurveType(test_group["key"]["curve"]);
146 HashType md = WycheproofUtil::GetHashType(test_group["sha"]);
147 auto result = EcdsaVerifyBoringSsl::New(key, md, encoding);
148 if (!result.ok()) {
149 std::cout << "Failed: " << result.status() << "\n";
150 }
151 return result;
152 }
153
154 // Tests signature verification using the test vectors in the specified file.
155 // allow_skipping determines whether it is OK to skip a test because
156 // a verfier cannot be constructed. This option can be used for
157 // if a file contains test vectors that are not necessarily supported
158 // by tink.
TestSignatures(const std::string & filename,bool allow_skipping,subtle::EcdsaSignatureEncoding encoding)159 bool TestSignatures(const std::string& filename, bool allow_skipping,
160 subtle::EcdsaSignatureEncoding encoding) {
161 std::unique_ptr<rapidjson::Document> root =
162 WycheproofUtil::ReadTestVectors(filename);
163 std::cout << (*root)["algorithm"].GetString();
164 std::cout << "generator version " << (*root)["generatorVersion"].GetString();
165 std::cout << "expected version 0.2.5";
166 int passed_tests = 0;
167 int failed_tests = 0;
168 for (const rapidjson::Value& test_group : (*root)["testGroups"].GetArray()) {
169 auto verifier_result = GetVerifier(test_group, encoding);
170 if (!verifier_result.ok()) {
171 std::string curve = test_group["key"]["curve"].GetString();
172 if (allow_skipping) {
173 std::cout << "Could not construct verifier for curve " << curve
174 << verifier_result.status();
175 } else {
176 ADD_FAILURE() << "Could not construct verifier for curve " << curve
177 << verifier_result.status();
178 failed_tests += test_group["tests"].GetArray().Size();
179 }
180 continue;
181 }
182 auto verifier = std::move(verifier_result.value());
183 for (const rapidjson::Value& test : test_group["tests"].GetArray()) {
184 std::string expected = test["result"].GetString();
185 std::string msg = WycheproofUtil::GetBytes(test["msg"]);
186 std::string sig = WycheproofUtil::GetBytes(test["sig"]);
187 std::string id =
188 absl::StrCat(test["tcId"].GetInt(), " ", test["comment"].GetString());
189 auto status = verifier->Verify(sig, msg);
190 if (expected == "valid") {
191 if (status.ok()) {
192 ++passed_tests;
193 } else {
194 ++failed_tests;
195 ADD_FAILURE() << "Valid signature not verified:" << id
196 << " status:" << status;
197 }
198 } else if (expected == "invalid") {
199 if (!status.ok()) {
200 ++passed_tests;
201 } else {
202 ++failed_tests;
203 ADD_FAILURE() << "Invalid signature verified:" << id;
204 }
205 } else if (expected == "acceptable") {
206 // The validity of the signature is undefined. Hence the test passes
207 // but we log the result since we might still want to know if the
208 // library is strict or forgiving.
209 ++passed_tests;
210 std::cout << "Acceptable signature:" << id << ":" << status;
211 } else {
212 ++failed_tests;
213 ADD_FAILURE() << "Invalid field result:" << expected;
214 }
215 }
216 }
217 int num_tests = (*root)["numberOfTests"].GetInt();
218 std::cout << "total number of tests: " << num_tests;
219 std::cout << "number of tests passed:" << passed_tests;
220 std::cout << "number of tests failed:" << failed_tests;
221 return failed_tests == 0;
222 }
223
TEST_F(EcdsaVerifyBoringSslTest,WycheproofCurveP256)224 TEST_F(EcdsaVerifyBoringSslTest, WycheproofCurveP256) {
225 if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) {
226 GTEST_SKIP()
227 << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable.";
228 }
229 ASSERT_TRUE(TestSignatures("ecdsa_secp256r1_sha256_test.json", false,
230 subtle::EcdsaSignatureEncoding::DER));
231 }
232
TEST_F(EcdsaVerifyBoringSslTest,WycheproofCurveP384)233 TEST_F(EcdsaVerifyBoringSslTest, WycheproofCurveP384) {
234 if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) {
235 GTEST_SKIP()
236 << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable.";
237 }
238 ASSERT_TRUE(TestSignatures("ecdsa_secp384r1_sha512_test.json", false,
239 subtle::EcdsaSignatureEncoding::DER));
240 }
241
TEST_F(EcdsaVerifyBoringSslTest,WycheproofCurveP521)242 TEST_F(EcdsaVerifyBoringSslTest, WycheproofCurveP521) {
243 if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) {
244 GTEST_SKIP()
245 << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable.";
246 }
247 ASSERT_TRUE(TestSignatures("ecdsa_secp521r1_sha512_test.json", false,
248 subtle::EcdsaSignatureEncoding::DER));
249 }
250
TEST_F(EcdsaVerifyBoringSslTest,WycheproofWithIeeeP1363Encoding)251 TEST_F(EcdsaVerifyBoringSslTest, WycheproofWithIeeeP1363Encoding) {
252 if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) {
253 GTEST_SKIP()
254 << "Test is skipped if kOnlyUseFips but BoringCrypto is unavailable.";
255 }
256 ASSERT_TRUE(TestSignatures("ecdsa_webcrypto_test.json", true,
257 subtle::EcdsaSignatureEncoding::IEEE_P1363));
258 }
259
260 // FIPS-only mode test
TEST_F(EcdsaVerifyBoringSslTest,TestFipsFailWithoutBoringCrypto)261 TEST_F(EcdsaVerifyBoringSslTest, TestFipsFailWithoutBoringCrypto) {
262 if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) {
263 GTEST_SKIP()
264 << "Test assumes kOnlyUseFips but BoringCrypto is unavailable.";
265 }
266
267 auto ec_key =
268 SubtleUtilBoringSSL::GetNewEcKey(EllipticCurveType::NIST_P256).value();
269 EXPECT_THAT(EcdsaVerifyBoringSsl::New(ec_key, HashType::SHA256,
270 EcdsaSignatureEncoding::DER)
271 .status(),
272 StatusIs(absl::StatusCode::kInternal));
273
274 ec_key =
275 SubtleUtilBoringSSL::GetNewEcKey(EllipticCurveType::NIST_P384).value();
276 EXPECT_THAT(EcdsaVerifyBoringSsl::New(ec_key, HashType::SHA256,
277 EcdsaSignatureEncoding::DER)
278 .status(),
279 StatusIs(absl::StatusCode::kInternal));
280
281 ec_key =
282 SubtleUtilBoringSSL::GetNewEcKey(EllipticCurveType::NIST_P521).value();
283 EXPECT_THAT(EcdsaVerifyBoringSsl::New(ec_key, HashType::SHA256,
284 EcdsaSignatureEncoding::DER)
285 .status(),
286 StatusIs(absl::StatusCode::kInternal));
287 }
288
289 } // namespace
290 } // namespace subtle
291 } // namespace tink
292 } // namespace crypto
293