From 685d4270145a999d43e91665a158a4fe32323f5a Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Tue, 29 Oct 2024 13:55:27 -0400 Subject: [PATCH] Testing DSA paramgen, keygen, sign, verify --- crypto/evp_extra/evp_extra_test.cc | 239 +++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) diff --git a/crypto/evp_extra/evp_extra_test.cc b/crypto/evp_extra/evp_extra_test.cc index 4e87216c794..01f5690af31 100644 --- a/crypto/evp_extra/evp_extra_test.cc +++ b/crypto/evp_extra/evp_extra_test.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include "../test/file_test.h" #include "../test/test_util.h" @@ -3095,3 +3096,241 @@ TEST_P(PerParamgenCBTest, ParamgenCallbacks) { } } } + +static bssl::UniquePtr dsa_paramgen(int nbits, const EVP_MD* md, bool copy) { + bssl::UniquePtr params(nullptr); + EVP_PKEY* pkey_raw = NULL; + + auto maybe_copy = [&](bssl::UniquePtr *ctx) -> bool { + if (copy) { + ctx->reset(EVP_PKEY_CTX_dup(ctx->get())); + } + return *ctx != nullptr; + }; + + // Construct a EVP_PKEY_CTX + bssl::UniquePtr ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, nullptr)); + if(!ctx) { + goto dsa_paramgen_end; + } + if(!maybe_copy(&ctx)) { + goto dsa_paramgen_end; + } + // Initialize for paramgen + if(1 != EVP_PKEY_paramgen_init(ctx.get())) { + goto dsa_paramgen_end; + } + // Set the prime length + if(1 != EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx.get(), nbits)) { + goto dsa_paramgen_end; + } + // Set the generator + if(1 != EVP_PKEY_CTX_set_dsa_paramgen_md(ctx.get(), md)) { + goto dsa_paramgen_end; + } + if(1 != EVP_PKEY_paramgen(ctx.get(), &pkey_raw)) { + goto dsa_paramgen_end; + } + params.reset(pkey_raw); +dsa_paramgen_end: + return params; +} + +static bssl::UniquePtr dsa_keygen(bssl::UniquePtr ¶ms, bool copy) { + bssl::UniquePtr pkey(nullptr); + EVP_PKEY *pkey_raw = nullptr; + + auto maybe_copy = [&](bssl::UniquePtr *ctx) -> bool { + if (copy) { + ctx->reset(EVP_PKEY_CTX_dup(ctx->get())); + } + return *ctx != nullptr; + }; + + bssl::UniquePtr ctx(EVP_PKEY_CTX_new(params.get(), nullptr)); + if(!ctx) { + goto dsa_keygen_end; + } + if(1 != EVP_PKEY_keygen_init(ctx.get())) { + goto dsa_keygen_end; + } + if(!maybe_copy(&ctx)) { + goto dsa_keygen_end; + } + if(1 != EVP_PKEY_keygen(ctx.get(), &pkey_raw)) { + goto dsa_keygen_end; + } + pkey.reset(pkey_raw); +dsa_keygen_end: + return pkey; +} + +static bssl::UniquePtr dsa_public_key(bssl::UniquePtr &private_key) { + bssl::UniquePtr pkey(nullptr); + bssl::UniquePtr bio(BIO_new(BIO_s_mem())); + if (!bio) { + goto dsa_public_key_end; + } + if (1 != PEM_write_bio_PUBKEY(bio.get(), private_key.get())) { + goto dsa_public_key_end; + } + pkey.reset(PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr)); + +dsa_public_key_end: + return pkey; +} + +TEST(EVPExtraTest, DSAKeygen) { + for (bool copy : {false, true}) { + SCOPED_TRACE(copy); + + bssl::UniquePtr params = dsa_paramgen(512, EVP_sha1(), copy); + ASSERT_TRUE(params); + const DSA* params_dsa = EVP_PKEY_get0_DSA(params.get()); + + bssl::UniquePtr pkey1 = dsa_keygen(params, copy); + ASSERT_TRUE(pkey1); + + EXPECT_EQ(EVP_PKEY_id(pkey1.get()), EVP_PKEY_DSA); + const DSA *key1_dsa = EVP_PKEY_get0_DSA(pkey1.get()); + // Parameters P, Q and G are shared + EXPECT_EQ(0, BN_cmp(DSA_get0_p(key1_dsa), DSA_get0_p(params_dsa))); + EXPECT_EQ(0, BN_cmp(DSA_get0_q(key1_dsa), DSA_get0_q(params_dsa))); + EXPECT_EQ(0, BN_cmp(DSA_get0_g(key1_dsa), DSA_get0_g(params_dsa))); + + EXPECT_TRUE(DSA_get0_pub_key(key1_dsa)); + EXPECT_TRUE(DSA_get0_priv_key(key1_dsa)); + EXPECT_EQ(1, EVP_PKEY_cmp_parameters(params.get(), pkey1.get())); + EXPECT_EQ(0, EVP_PKEY_cmp(params.get(), pkey1.get())); + + // Generate a second key. + bssl::UniquePtr pkey2 = dsa_keygen(params, copy); + ASSERT_TRUE(pkey2); + + const DSA *key2_dsa = EVP_PKEY_get0_DSA(pkey2.get()); + EXPECT_EQ(0, BN_cmp(DSA_get0_p(key1_dsa), DSA_get0_p(key2_dsa))); + EXPECT_EQ(0, BN_cmp(DSA_get0_q(key1_dsa), DSA_get0_q(key2_dsa))); + EXPECT_EQ(0, BN_cmp(DSA_get0_g(key1_dsa), DSA_get0_g(key2_dsa))); + EXPECT_TRUE(DSA_get0_pub_key(key1_dsa)); + EXPECT_TRUE(DSA_get0_priv_key(key1_dsa)); + + EXPECT_EQ(1, EVP_PKEY_cmp_parameters(params.get(), pkey2.get())); + EXPECT_EQ(1, EVP_PKEY_cmp_parameters(pkey1.get(), pkey2.get())); + EXPECT_EQ(0, EVP_PKEY_cmp(pkey1.get(), pkey2.get())); + } +} + +TEST(EVPExtraTest, DSAParamgen) { + std::vector> test_data( + {{768, EVP_sha1()}, {2048, EVP_sha224()}, {512, EVP_sha256()}}); + + for (std::pair plgen : test_data) { + const int nbits = plgen.first; + const EVP_MD* digest = plgen.second; + // Construct a EVP_PKEY_CTX + bssl::UniquePtr ctx( + EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, nullptr)); + ASSERT_TRUE(ctx); + // Initialize for paramgen + ASSERT_TRUE(EVP_PKEY_paramgen_init(ctx.get())); + // Set the prime nbits + ASSERT_TRUE(EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx.get(), nbits)); + // Set the digest + ASSERT_TRUE(EVP_PKEY_CTX_set_dsa_paramgen_md(ctx.get(), digest)); + + EVP_PKEY *raw_pkey = NULL; + // Generate the parameters + ASSERT_TRUE(EVP_PKEY_paramgen(ctx.get(), &raw_pkey)); + bssl::UniquePtr pkey(raw_pkey); + ASSERT_TRUE(pkey); + + const DSA* dsa = EVP_PKEY_get0_DSA(pkey.get()); + ASSERT_TRUE(dsa); + const BIGNUM* p = DSA_get0_p(dsa); + ASSERT_TRUE(p); + unsigned p_size = BN_num_bits(p); + ASSERT_EQ(p_size, (unsigned)nbits); + + const BIGNUM* q = DSA_get0_q(dsa); + ASSERT_TRUE(q); + unsigned q_size = BN_num_bits(q); + ASSERT_EQ(q_size, 8*(unsigned)EVP_MD_size(digest)); + } + + // Test error conditions + // Construct a EVP_PKEY_CTX + bssl::UniquePtr ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, nullptr)); + ASSERT_TRUE(ctx); + // Initialize for paramgen + ASSERT_TRUE(EVP_PKEY_paramgen_init(ctx.get())); + // Set the prime length + ASSERT_NE(EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx.get(), 511), 1); + // Set the generator + ASSERT_NE(EVP_PKEY_CTX_set_dsa_paramgen_md(ctx.get(), EVP_md5()), 1); +} + +TEST(EVPExtraTest, DSASignDigestVerify) { + bssl::UniquePtr params = dsa_paramgen(512, EVP_sha1(), false); + ASSERT_TRUE(params); + + bssl::UniquePtr private_key = dsa_keygen(params, false); + ASSERT_TRUE(private_key); + + const char data[] = "Sign Me!"; + uint8_t digest[32] = {0}; + std::vector sig; + size_t siglen = 0; + ASSERT_TRUE(SHA1((uint8_t*)data, strnlen(data, 9), digest)); + { + bssl::UniquePtr ctx(EVP_PKEY_CTX_new(private_key.get(), nullptr)); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_PKEY_sign_init(ctx.get())); + ASSERT_EQ(1, EVP_PKEY_sign(ctx.get(), NULL, &siglen, digest, 32)); + ASSERT_GT(siglen, (size_t)0); + sig.resize(siglen); + ASSERT_EQ(1, EVP_PKEY_sign(ctx.get(), sig.data(), &siglen, digest, 32)); + } + + bssl::UniquePtr public_key = dsa_public_key(private_key); + { + bssl::UniquePtr md_ctx(EVP_MD_CTX_new()); + ASSERT_EQ(1, EVP_DigestVerifyInit(md_ctx.get(), nullptr, EVP_sha1(), nullptr, public_key.get())); + ASSERT_EQ(1, EVP_DigestVerifyUpdate(md_ctx.get(), data, strnlen(data, 9))); + ASSERT_EQ(1, EVP_DigestVerifyFinal(md_ctx.get(), sig.data(), sig.size())); + } + +} + +TEST(EVPExtraTest, DSADigestSignVerify) { + bssl::UniquePtr params = dsa_paramgen(512, EVP_sha1(), false); + ASSERT_TRUE(params); + + bssl::UniquePtr private_key = dsa_keygen(params, false); + ASSERT_TRUE(private_key); + + const char data[] = "Sign Me!"; + std::vector sig; + size_t siglen = 0; + + { + bssl::UniquePtr md_ctx(EVP_MD_CTX_new()); + ASSERT_TRUE(md_ctx); + ASSERT_NE(1, EVP_DigestSignInit(md_ctx.get(), nullptr, EVP_md5(), nullptr, private_key.get())); + ASSERT_EQ(1, EVP_DigestSignInit(md_ctx.get(), nullptr, EVP_sha256(), nullptr, private_key.get())); + ASSERT_EQ(1, EVP_DigestSignUpdate(md_ctx.get(), data, strnlen(data, 9))); + ASSERT_EQ(1, EVP_DigestSignFinal(md_ctx.get(), nullptr, &siglen)); + sig.resize(siglen); + ASSERT_EQ(1, EVP_DigestSignFinal(md_ctx.get(), sig.data(), &siglen)); + } + + uint8_t digest[32] = {0}; + ASSERT_TRUE(SHA256((uint8_t*)data, strnlen(data, 9), digest)); + bssl::UniquePtr public_key = dsa_public_key(private_key); + { + bssl::UniquePtr ctx(EVP_PKEY_CTX_new(public_key.get(), nullptr)); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_PKEY_verify_init(ctx.get())); + ASSERT_EQ(1, EVP_PKEY_verify(ctx.get(), sig.data(), siglen, digest, 32)); + } + +}