| // Copyright 2025 The BoringSSL Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <stdio.h> |
| |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| |
| #include <openssl/crypto.h> |
| #include <openssl/ec.h> |
| #include <openssl/ec_key.h> |
| #include <openssl/ecdsa.h> |
| #include <openssl/evp.h> |
| #include <openssl/rand.h> |
| |
| #include "../test/file_test.h" |
| #include "../test/wycheproof_util.h" |
| |
| |
| static void RunWycheproofTest(const char *path) { |
| SCOPED_TRACE(path); |
| FileTestGTest(path, [](FileTest *t) { |
| t->IgnoreAllUnusedInstructions(); |
| |
| const EC_GROUP *group = GetWycheproofCurve(t, "key.curve", true); |
| ASSERT_TRUE(group); |
| std::vector<uint8_t> uncompressed; |
| ASSERT_TRUE(t->GetInstructionBytes(&uncompressed, "key.uncompressed")); |
| bssl::UniquePtr<EC_KEY> key(EC_KEY_new()); |
| ASSERT_TRUE(key); |
| ASSERT_TRUE(EC_KEY_set_group(key.get(), group)); |
| ASSERT_TRUE(EC_KEY_oct2key(key.get(), uncompressed.data(), |
| uncompressed.size(), nullptr)); |
| |
| const EVP_MD *md = GetWycheproofDigest(t, "sha", true); |
| ASSERT_TRUE(md); |
| |
| std::vector<uint8_t> msg; |
| ASSERT_TRUE(t->GetBytes(&msg, "msg")); |
| std::vector<uint8_t> sig; |
| ASSERT_TRUE(t->GetBytes(&sig, "sig")); |
| WycheproofResult result; |
| ASSERT_TRUE(GetWycheproofResult(t, &result)); |
| |
| uint8_t digest[EVP_MAX_MD_SIZE]; |
| unsigned int digest_len; |
| ASSERT_TRUE( |
| EVP_Digest(msg.data(), msg.size(), digest, &digest_len, md, nullptr)); |
| |
| int ret = ECDSA_verify_p1363(digest, digest_len, sig.data(), sig.size(), |
| key.get()); |
| EXPECT_EQ(ret, result.IsValid() ? 1 : 0); |
| }); |
| } |
| |
| TEST(ECDSAP1363Test, WycheproofP224) { |
| RunWycheproofTest( |
| "third_party/wycheproof_testvectors/" |
| "ecdsa_secp224r1_sha224_p1363_test.txt"); |
| RunWycheproofTest( |
| "third_party/wycheproof_testvectors/" |
| "ecdsa_secp224r1_sha256_p1363_test.txt"); |
| RunWycheproofTest( |
| "third_party/wycheproof_testvectors/" |
| "ecdsa_secp224r1_sha512_p1363_test.txt"); |
| } |
| |
| TEST(ECDSAP1363Test, WycheproofP256) { |
| RunWycheproofTest( |
| "third_party/wycheproof_testvectors/" |
| "ecdsa_secp256r1_sha256_p1363_test.txt"); |
| RunWycheproofTest( |
| "third_party/wycheproof_testvectors/" |
| "ecdsa_secp256r1_sha512_p1363_test.txt"); |
| } |
| |
| TEST(ECDSAP1363Test, WycheproofP384) { |
| RunWycheproofTest( |
| "third_party/wycheproof_testvectors/" |
| "ecdsa_secp384r1_sha384_p1363_test.txt"); |
| RunWycheproofTest( |
| "third_party/wycheproof_testvectors/" |
| "ecdsa_secp384r1_sha512_p1363_test.txt"); |
| } |
| |
| TEST(ECDSAP1363Test, WycheproofP521) { |
| RunWycheproofTest( |
| "third_party/wycheproof_testvectors/" |
| "ecdsa_secp521r1_sha512_p1363_test.txt"); |
| } |
| |
| |
| static void RunSignTest(const EC_GROUP *group) { |
| // Fill digest values with some random data. |
| uint8_t digest[20]; |
| ASSERT_TRUE(RAND_bytes(digest, sizeof(digest))); |
| |
| bssl::UniquePtr<EC_KEY> key(EC_KEY_new()); |
| ASSERT_TRUE(key); |
| ASSERT_TRUE(EC_KEY_set_group(key.get(), group)); |
| ASSERT_TRUE(EC_KEY_generate_key(key.get())); |
| |
| size_t sig_len = ECDSA_size_p1363(key.get()); |
| ASSERT_GT(sig_len, 0u); |
| std::vector<uint8_t> sig(sig_len); |
| |
| size_t out_sig_len; |
| ASSERT_TRUE(ECDSA_sign_p1363(digest, sizeof(digest), sig.data(), &out_sig_len, |
| sig.size(), key.get())); |
| ASSERT_EQ(out_sig_len, sig_len); |
| |
| ASSERT_TRUE(ECDSA_verify_p1363(digest, sizeof(digest), sig.data(), sig.size(), |
| key.get())); |
| } |
| |
| TEST(ECDSAP1363Test, SignP224) { |
| RunSignTest(EC_group_p224()); |
| } |
| |
| TEST(ECDSAP1363Test, SignP256) { |
| RunSignTest(EC_group_p256()); |
| } |
| |
| TEST(ECDSAP1363Test, SignP384) { |
| RunSignTest(EC_group_p384()); |
| } |
| |
| TEST(ECDSAP1363Test, SignP521) { |
| RunSignTest(EC_group_p521()); |
| } |
| |
| TEST(ECDSAP1363Test, SignFailsWithSmallBuffer) { |
| // Fill digest values with some random data. |
| uint8_t digest[20]; |
| ASSERT_TRUE(RAND_bytes(digest, sizeof(digest))); |
| |
| bssl::UniquePtr<EC_KEY> key(EC_KEY_new()); |
| ASSERT_TRUE(key); |
| ASSERT_TRUE(EC_KEY_set_group(key.get(), EC_group_p256())); |
| ASSERT_TRUE(EC_KEY_generate_key(key.get())); |
| |
| size_t sig_len = ECDSA_size_p1363(key.get()); |
| ASSERT_GT(sig_len, 0u); |
| std::vector<uint8_t> sig(sig_len - 1); |
| |
| size_t out_sig_len; |
| ASSERT_FALSE(ECDSA_sign_p1363(digest, sizeof(digest), sig.data(), |
| &out_sig_len, sig.size(), key.get())); |
| } |
| |
| TEST(ECDSAP1363Test, SignSucceedsWithLargeBuffer) { |
| // Fill digest values with some random data. |
| uint8_t digest[20]; |
| ASSERT_TRUE(RAND_bytes(digest, sizeof(digest))); |
| |
| bssl::UniquePtr<EC_KEY> key(EC_KEY_new()); |
| ASSERT_TRUE(key); |
| ASSERT_TRUE(EC_KEY_set_group(key.get(), EC_group_p256())); |
| ASSERT_TRUE(EC_KEY_generate_key(key.get())); |
| |
| size_t sig_len = ECDSA_size_p1363(key.get()); |
| ASSERT_GT(sig_len, 0u); |
| std::vector<uint8_t> sig(sig_len + 1, 'x'); |
| |
| size_t out_sig_len; |
| ASSERT_TRUE(ECDSA_sign_p1363(digest, sizeof(digest), sig.data(), |
| &out_sig_len, sig.size(), key.get())); |
| ASSERT_EQ(out_sig_len, sig_len); |
| // The extra byte should be untouched. |
| EXPECT_EQ(sig.back(), 'x'); |
| |
| ASSERT_TRUE(ECDSA_verify_p1363(digest, sizeof(digest), sig.data(), |
| out_sig_len, key.get())); |
| } |
| |
| TEST(ECDSAP1363Test, SizeWithoutGroup) { |
| EXPECT_EQ(ECDSA_size_p1363(nullptr), 0u); |
| |
| bssl::UniquePtr<EC_KEY> key(EC_KEY_new()); |
| EXPECT_EQ(ECDSA_size_p1363(key.get()), 0u); |
| } |