diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index e546172feb..db3a743f02 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -420,6 +420,7 @@ add_library( evp_extra/evp_asn1.c evp_extra/p_dh.c evp_extra/p_dh_asn1.c + evp_extra/p_dsa.c evp_extra/p_dsa_asn1.c evp_extra/p_ec_asn1.c evp_extra/p_ed25519_asn1.c diff --git a/crypto/evp_extra/evp_extra_test.cc b/crypto/evp_extra/evp_extra_test.cc index 62b6297071..9308fe2e42 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" @@ -3025,9 +3026,30 @@ TEST(EVPExtraTest, KeygenCallbacks) { } } +struct ParamgenCBParam { + const char* name; + int pkey_type; + const char* setup_command; + const char* setup_arg; + int keygen_info_0; + int keygen_into_1; +}; -TEST(EVPExtraTest, ParamgenCallbacks) { - bssl::UniquePtr ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr)); +static const ParamgenCBParam paramgenCBparams[] = { + // DH_generate_parameters_ex makes a final call to `BN_GENCB_call(cb, 3, 0)` + {"DH", EVP_PKEY_DH, "dh_paramgen_prime_len", "512", 3, 0}, + // dsa_internal_paramgen makes a final call to `BN_GENCB_call(cb, 3, 1))` + {"DSA", EVP_PKEY_DSA, "dsa_paramgen_bits", "512", 3, 1}, +}; + +class PerParamgenCBTest : public testing::TestWithParam {}; + +INSTANTIATE_TEST_SUITE_P(All, PerParamgenCBTest, testing::ValuesIn(paramgenCBparams), + [](const testing::TestParamInfo ¶ms) + -> const char* { return params.param.name; }); + +TEST_P(PerParamgenCBTest, ParamgenCallbacks) { + bssl::UniquePtr ctx(EVP_PKEY_CTX_new_id(GetParam().pkey_type, nullptr)); ASSERT_TRUE(ctx); // Check the initial values of |ctx->keygen_info|. @@ -3041,8 +3063,7 @@ TEST(EVPExtraTest, ParamgenCallbacks) { // Generating an DH params will trigger the callback. EVP_PKEY *pkey = EVP_PKEY_new(); ASSERT_EQ(EVP_PKEY_paramgen_init(ctx.get()), 1); - - ASSERT_TRUE(EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx.get(), 512)); + ASSERT_TRUE(EVP_PKEY_CTX_ctrl_str(ctx.get(), GetParam().setup_command, GetParam().setup_arg)); ASSERT_TRUE(EVP_PKEY_paramgen(ctx.get(), &pkey)); ASSERT_TRUE(pkey); @@ -3068,12 +3089,262 @@ TEST(EVPExtraTest, ParamgenCallbacks) { EXPECT_TRUE(app_data.state); for (int i = 0; i < keygen_info; i++) { - // DH_generate_parameters_ex makes a final call to `BN_GENCB_call(cb, 3, 0)` if(i == 0) { - EXPECT_EQ(EVP_PKEY_CTX_get_keygen_info(ctx.get(), i), 3); + EXPECT_EQ(EVP_PKEY_CTX_get_keygen_info(ctx.get(), i), GetParam().keygen_info_0); } else { - EXPECT_EQ(EVP_PKEY_CTX_get_keygen_info(ctx.get(), i), 0); + EXPECT_EQ(EVP_PKEY_CTX_get_keygen_info(ctx.get(), i), GetParam().keygen_into_1); + } + } +} + +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 && maybe_copy(&ctx) && + 1 == EVP_PKEY_paramgen_init(ctx.get()) && + 1 == EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx.get(), nbits) && + 1 == EVP_PKEY_CTX_set_dsa_paramgen_md(ctx.get(), md) && + 1 == EVP_PKEY_paramgen(ctx.get(), &pkey_raw)) { + params.reset(pkey_raw); + } + 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 && + 1 == EVP_PKEY_keygen_init(ctx.get()) && maybe_copy(&ctx) && + 1 == EVP_PKEY_keygen(ctx.get(), &pkey_raw)) { + pkey.reset(pkey_raw); + } + 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 && + 1 == PEM_write_bio_PUBKEY(bio.get(), private_key.get())) { + pkey.reset(PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr)); + } + 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!"; + const size_t data_len = strnlen(data, 9); + uint8_t digest[32] = {0}; + std::vector sig; + size_t siglen = 0; + ASSERT_TRUE(SHA1((uint8_t*)data, data_len, 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)); + } + // This intentionally does not use EVP_PKEY_verify to help ensure the + // equivalence of using different APIs for the same purpose. + 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, data_len)); + ASSERT_EQ(1, EVP_DigestVerifyFinal(md_ctx.get(), sig.data(), sig.size())); + } + +} + +TEST(EVPExtraTest, DSADigestSignFinalVerify) { + 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!"; + const size_t data_len = strnlen(data, 9); + std::vector sig; + size_t siglen = 0; + + { + EVP_PKEY_CTX* raw_pctx = nullptr; + const EVP_MD* raw_md = nullptr; + + bssl::UniquePtr md_ctx(EVP_MD_CTX_new()); + ASSERT_TRUE(md_ctx); + ASSERT_NE(1, EVP_DigestSignInit(md_ctx.get(), &raw_pctx, EVP_md5(), nullptr, private_key.get())); + // md_ctx takes ownership of raw_pctx + ASSERT_EQ(1, EVP_DigestSignInit(md_ctx.get(), &raw_pctx, EVP_sha256(), nullptr, private_key.get())); + + ASSERT_EQ(1, EVP_PKEY_CTX_get_signature_md(raw_pctx, &raw_md)); + ASSERT_EQ(EVP_sha256(), raw_md); + + ASSERT_EQ(1, EVP_DigestSignUpdate(md_ctx.get(), data, data_len)); + ASSERT_EQ(1, EVP_DigestSignFinal(md_ctx.get(), nullptr, &siglen)); + sig.resize(siglen); + ASSERT_EQ(1, EVP_DigestSignFinal(md_ctx.get(), sig.data(), &siglen)); + } + // This intentionally does not use EVP_DigestVerify to help ensure the + // equivalence of using different APIs for the same purpose. + uint8_t digest[32] = {0}; + ASSERT_TRUE(SHA256((uint8_t*)data, data_len, 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)); } } +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!"; + const size_t data_len = strnlen(data, 9); + std::vector sig; + size_t siglen = 0; + + { + bssl::UniquePtr md_ctx(EVP_MD_CTX_new()); + ASSERT_TRUE(md_ctx); + ASSERT_EQ(1, EVP_DigestSignInit(md_ctx.get(), nullptr, EVP_sha256(), nullptr, private_key.get())); + ASSERT_EQ(1, EVP_DigestSign(md_ctx.get(), nullptr, &siglen, (const uint8_t*)data, data_len)); + sig.resize(siglen); + ASSERT_EQ(1, EVP_DigestSign(md_ctx.get(), sig.data(), &siglen, (const uint8_t*)data, data_len)); + } + + bssl::UniquePtr public_key = dsa_public_key(private_key); + { + bssl::UniquePtr md_ctx(EVP_MD_CTX_new()); + ASSERT_TRUE(md_ctx); + ASSERT_TRUE(EVP_DigestVerifyInit(md_ctx.get(), nullptr, EVP_sha256(), nullptr, public_key.get())); + ASSERT_TRUE(EVP_DigestVerify(md_ctx.get(), sig.data(), sig.size(), (const uint8_t*)data, data_len)); + } +} diff --git a/crypto/evp_extra/internal.h b/crypto/evp_extra/internal.h index 85b4f123df..b645330a62 100644 --- a/crypto/evp_extra/internal.h +++ b/crypto/evp_extra/internal.h @@ -48,6 +48,7 @@ extern const EVP_PKEY_METHOD hkdf_pkey_meth; extern const EVP_PKEY_METHOD dilithium3_pkey_meth; extern const EVP_PKEY_METHOD hmac_pkey_meth; extern const EVP_PKEY_METHOD dh_pkey_meth; +extern const EVP_PKEY_METHOD dsa_pkey_meth; // evp_pkey_set_method behaves like |EVP_PKEY_set_type|, but takes a pointer to // a method table. This avoids depending on every |EVP_PKEY_ASN1_METHOD|. diff --git a/crypto/evp_extra/p_dsa.c b/crypto/evp_extra/p_dsa.c new file mode 100644 index 0000000000..194916fa27 --- /dev/null +++ b/crypto/evp_extra/p_dsa.c @@ -0,0 +1,358 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +#include +#include +#include + +#include "../dsa/internal.h" +#include "../internal.h" +#include "./internal.h" + +typedef struct { + int nbits; // defaults to 2048 + int qbits; + const EVP_MD *pmd; // MD for paramgen + const EVP_MD *md; // MD for signing +} DSA_PKEY_CTX; + +static int pkey_dsa_init(EVP_PKEY_CTX *ctx) { + DSA_PKEY_CTX *dctx = NULL; + if (!(dctx = (DSA_PKEY_CTX *)OPENSSL_zalloc(sizeof(DSA_PKEY_CTX)))) { + return 0; + } + dctx->nbits = 2048; + dctx->qbits = 256; + dctx->pmd = NULL; + dctx->md = NULL; + + ctx->data = dctx; + + return 1; +} + +static int pkey_dsa_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) { + DSA_PKEY_CTX *dctx = NULL; + DSA_PKEY_CTX *sctx = NULL; + if (!pkey_dsa_init(dst)) { + return 0; + } + sctx = (DSA_PKEY_CTX *)src->data; + dctx = (DSA_PKEY_CTX *)dst->data; + if (sctx == NULL || dctx == NULL) { + return 0; + } + + dctx->nbits = sctx->nbits; + dctx->qbits = sctx->qbits; + dctx->pmd = sctx->pmd; + dctx->md = sctx->md; + return 1; +} + +static void pkey_dsa_cleanup(EVP_PKEY_CTX *ctx) { + OPENSSL_free(ctx->data); + ctx->data = NULL; +} + +static int pkey_dsa_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) { + GUARD_PTR(ctx->pkey); + + int ret = 0; + DSA *dsa = NULL; + if (!((dsa = DSA_new())) || !EVP_PKEY_assign_DSA(pkey, dsa) || + !EVP_PKEY_copy_parameters(pkey, ctx->pkey)) { + goto err; + } + ret = DSA_generate_key(pkey->pkey.dsa); + +err: + if (ret != 1) { + OPENSSL_free(dsa); + } + return ret; +} + +static int pkey_dsa_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) { + BN_GENCB *pkey_ctx_cb = NULL; + DSA *dsa = NULL; + DSA_PKEY_CTX *dctx = (DSA_PKEY_CTX *)ctx->data; + GUARD_PTR(dctx); + + int ret = 0; + + if (ctx->pkey_gencb) { + pkey_ctx_cb = BN_GENCB_new(); + if (pkey_ctx_cb == NULL) { + goto end; + } + evp_pkey_set_cb_translate(pkey_ctx_cb, ctx); + } + + const EVP_MD *pmd = dctx->pmd; + if (pmd == NULL) { + switch (dctx->qbits) { + case 160: + pmd = EVP_sha1(); + break; + case 224: + pmd = EVP_sha224(); + break; + case 256: + pmd = EVP_sha256(); + break; + default: + // This should not be possible. + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_OPERATION); + goto end; + } + } + + if (!((dsa = DSA_new()))) { + goto end; + } + + if (!dsa_internal_paramgen(dsa, dctx->nbits, pmd, NULL, 0, NULL, NULL, + pkey_ctx_cb)) { + goto end; + } + + ret = EVP_PKEY_assign_DSA(pkey, dsa); +end: + BN_GENCB_free(pkey_ctx_cb); + if (ret != 1) { + OPENSSL_free(dsa); + } + + return ret; +} + +static int pkey_dsa_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen) { + GUARD_PTR(ctx->pkey); + GUARD_PTR(ctx->pkey->pkey.ptr); + GUARD_PTR(ctx->data); + GUARD_PTR(siglen); + + DSA_PKEY_CTX *dctx = ctx->data; + DSA *dsa = ctx->pkey->pkey.dsa; + + if (sig == NULL) { + // Passing NULL for sig indicates a query for the size of the signature + *siglen = DSA_size(dsa); + return 1; + } + + DSA_SIG *result = NULL; + uint8_t *sig_buffer = NULL; + int retval = 0; + + if (dctx->md != NULL && tbslen != EVP_MD_size(dctx->md)) { + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PARAMETERS); + goto end; + } + + result = DSA_do_sign(tbs, tbslen, dsa); + if (result == NULL) { + goto end; + } + CBB sig_bytes; + if (1 != CBB_init(&sig_bytes, tbslen)) { + goto end; + } + DSA_SIG_marshal(&sig_bytes, result); + if (1 != CBB_finish(&sig_bytes, &sig_buffer, siglen)) { + goto end; + } + OPENSSL_memcpy(sig, sig_buffer, *siglen); + retval = 1; +end: + OPENSSL_free(sig_buffer); + DSA_SIG_free(result); + return retval; +} + +static int pkey_dsa_verify(EVP_PKEY_CTX *ctx, const unsigned char *sig, + size_t siglen, const unsigned char *tbs, + size_t tbslen) { + GUARD_PTR(ctx->pkey); + GUARD_PTR(ctx->pkey->pkey.ptr); + GUARD_PTR(ctx->data); + GUARD_PTR(tbs); + + DSA_PKEY_CTX *dctx = ctx->data; + const DSA *dsa = ctx->pkey->pkey.dsa; + + if (dctx->md != NULL && tbslen != EVP_MD_size(dctx->md)) { + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PARAMETERS); + return 0; + } + + DSA_SIG *dsa_sig = NULL; + int retval = 0; + + CBS sig_cbs; + CBS_init(&sig_cbs, sig, siglen); + dsa_sig = DSA_SIG_parse(&sig_cbs); + if (dsa_sig == NULL) { + goto end; + } + if (1 != DSA_do_verify(tbs, tbslen, dsa_sig, dsa)) { + goto end; + } + retval = 1; +end: + DSA_SIG_free(dsa_sig); + return retval; +} + +static int pkey_dsa_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) { + DSA_PKEY_CTX *dctx = ctx->data; + + switch (type) { + case EVP_PKEY_CTRL_DSA_PARAMGEN_BITS: + if (p1 < 512) { + return -2; + } + dctx->nbits = p1; + return 1; + + case EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS: { + switch (p1) { + case 160: + case 224: + case 256: + dctx->qbits = p1; + return 1; + default: + return -2; + } + } + + case EVP_PKEY_CTRL_DSA_PARAMGEN_MD: { + const EVP_MD *pmd = (const EVP_MD *)p2; + if (pmd == NULL) { + return 0; + } + switch (EVP_MD_type(pmd)) { + case NID_sha1: + case NID_sha224: + case NID_sha256: + dctx->pmd = pmd; + return 1; + default: + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_DIGEST_TYPE); + return 0; + } + } + case EVP_PKEY_CTRL_MD: { + const EVP_MD *md = (const EVP_MD *)p2; + if (md == NULL) { + return 0; + } + switch (EVP_MD_type(md)) { + case NID_sha1: + case NID_sha224: + case NID_sha256: + case NID_sha384: + case NID_sha512: + case NID_sha3_224: + case NID_sha3_256: + case NID_sha3_384: + case NID_sha3_512: + dctx->md = md; + return 1; + default: + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_DIGEST_TYPE); + return 0; + } + } + case EVP_PKEY_CTRL_GET_MD: + if (p2 == NULL) { + return 0; + } + *(const EVP_MD **)p2 = dctx->md; + return 1; + + case EVP_PKEY_CTRL_PEER_KEY: + OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + return -2; + default: + OPENSSL_PUT_ERROR(EVP, EVP_R_COMMAND_NOT_SUPPORTED); + return 0; + } +} + +static int pkey_dsa_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, + const char *value) { + if (strcmp(type, "dsa_paramgen_bits") == 0) { + char *str_end = NULL; + long nbits = strtol(value, &str_end, 10); + if (str_end == value || nbits < 0 || nbits > INT_MAX) { + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_OPERATION); + return 0; + } +OPENSSL_BEGIN_ALLOW_DEPRECATED + return EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx, (int)nbits); +OPENSSL_END_ALLOW_DEPRECATED + } + if (strcmp(type, "dsa_paramgen_q_bits") == 0) { + char *str_end = NULL; + long qbits = strtol(value, &str_end, 10); + if (str_end == value || qbits < 0 || qbits > INT_MAX) { + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_OPERATION); + return 0; + } +OPENSSL_BEGIN_ALLOW_DEPRECATED + return EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx, (int)qbits); +OPENSSL_END_ALLOW_DEPRECATED + } + if (strcmp(type, "dsa_paramgen_md") == 0) { + const EVP_MD *md = EVP_get_digestbyname(value); + + if (md == NULL) { + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_DIGEST_TYPE); + return 0; + } +OPENSSL_BEGIN_ALLOW_DEPRECATED + return EVP_PKEY_CTX_set_dsa_paramgen_md(ctx, md); +OPENSSL_END_ALLOW_DEPRECATED + } + return -2; +} + +const EVP_PKEY_METHOD dsa_pkey_meth = {.pkey_id = EVP_PKEY_DSA, + .init = pkey_dsa_init, + .copy = pkey_dsa_copy, + .cleanup = pkey_dsa_cleanup, + .keygen = pkey_dsa_keygen, + .paramgen = pkey_dsa_paramgen, + .sign = pkey_dsa_sign, + .verify = pkey_dsa_verify, + .ctrl = pkey_dsa_ctrl, + .ctrl_str = pkey_dsa_ctrl_str}; + + +int EVP_PKEY_CTX_set_dsa_paramgen_bits(EVP_PKEY_CTX *ctx, int nbits) { + if (1 == EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DSA, EVP_PKEY_OP_PARAMGEN, + EVP_PKEY_CTRL_DSA_PARAMGEN_BITS, nbits, NULL)) { + return 1; + } + return 0; +} + +int EVP_PKEY_CTX_set_dsa_paramgen_q_bits(EVP_PKEY_CTX *ctx, int qbits) { + if (1 == EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DSA, EVP_PKEY_OP_PARAMGEN, + EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, qbits, NULL)) { + return 1; + } + return 0; +} + +int EVP_PKEY_CTX_set_dsa_paramgen_md(EVP_PKEY_CTX *ctx, const EVP_MD *md) { + if (1 == EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DSA, EVP_PKEY_OP_PARAMGEN, + EVP_PKEY_CTRL_DSA_PARAMGEN_MD, 0, (void *)md)) { + return 1; + } + return 0; +} diff --git a/crypto/evp_extra/p_dsa_asn1.c b/crypto/evp_extra/p_dsa_asn1.c index 9d927897b1..4369a484b6 100644 --- a/crypto/evp_extra/p_dsa_asn1.c +++ b/crypto/evp_extra/p_dsa_asn1.c @@ -287,15 +287,3 @@ const EVP_PKEY_ASN1_METHOD dsa_asn1_meth = { int_dsa_free, }; - -int EVP_PKEY_CTX_set_dsa_paramgen_bits(EVP_PKEY_CTX *ctx, int nbits) { - // BoringSSL does not support DSA in |EVP_PKEY_CTX|. - OPENSSL_PUT_ERROR(EVP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); - return 0; -} - -int EVP_PKEY_CTX_set_dsa_paramgen_q_bits(EVP_PKEY_CTX *ctx, int qbits) { - // BoringSSL does not support DSA in |EVP_PKEY_CTX|. - OPENSSL_PUT_ERROR(EVP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); - return 0; -} diff --git a/crypto/evp_extra/p_methods.c b/crypto/evp_extra/p_methods.c index a7e86ef657..ddfd818c8c 100644 --- a/crypto/evp_extra/p_methods.c +++ b/crypto/evp_extra/p_methods.c @@ -13,9 +13,9 @@ static const EVP_PKEY_METHOD *const non_fips_pkey_evp_methods[] = { &dilithium3_pkey_meth, #endif &dh_pkey_meth, + &dsa_pkey_meth }; -// We intentionally omit |dh_asn1_meth| from this list. It is not serializable. const EVP_PKEY_ASN1_METHOD *const asn1_evp_pkey_methods[] = { &rsa_asn1_meth, &rsa_pss_asn1_meth, diff --git a/crypto/fipsmodule/evp/digestsign.c b/crypto/fipsmodule/evp/digestsign.c index 914db61789..5b4a6e5e07 100644 --- a/crypto/fipsmodule/evp/digestsign.c +++ b/crypto/fipsmodule/evp/digestsign.c @@ -264,6 +264,7 @@ int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig, size_t sig_len) { int EVP_DigestSign(EVP_MD_CTX *ctx, uint8_t *out_sig, size_t *out_sig_len, const uint8_t *data, size_t data_len) { + GUARD_PTR(ctx->pctx); // We have to avoid the underlying |EVP_DigestSignFinal| services updating // the indicator state, so we lock the state here. FIPS_service_indicator_lock_state(); @@ -302,6 +303,7 @@ int EVP_DigestSign(EVP_MD_CTX *ctx, uint8_t *out_sig, size_t *out_sig_len, int EVP_DigestVerify(EVP_MD_CTX *ctx, const uint8_t *sig, size_t sig_len, const uint8_t *data, size_t len) { + GUARD_PTR(ctx->pctx); // We have to avoid the underlying |EVP_DigestSignFinal| services updating // the indicator state, so we lock the state here. FIPS_service_indicator_lock_state(); diff --git a/crypto/fipsmodule/evp/evp_ctx_test.cc b/crypto/fipsmodule/evp/evp_ctx_test.cc index 3f80fbdb87..d57635dce7 100644 --- a/crypto/fipsmodule/evp/evp_ctx_test.cc +++ b/crypto/fipsmodule/evp/evp_ctx_test.cc @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -415,3 +416,83 @@ TEST_F(EvpPkeyCtxCtrlStrTest, HMACKey) { } + +static void verify_DSA(const DSA* dsa, unsigned psize, unsigned qsize) { + const BIGNUM* p = DSA_get0_p(dsa); + EXPECT_TRUE(p != NULL); + if (p == NULL) { + return; + } + EXPECT_EQ(BN_num_bytes(p), psize); + const BIGNUM* q = DSA_get0_q(dsa); + EXPECT_TRUE(q != NULL); + if (q == NULL) { + return; + } + EXPECT_EQ(BN_num_bytes(q), qsize); +} + + +TEST_F(EvpPkeyCtxCtrlStrTest, DSAParamGen) { + + { + bssl::UniquePtr ctx( + EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, nullptr)); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_PKEY_paramgen_init(ctx.get())); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_bits", "512"), 1); + ASSERT_NE(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_bits", "256"), 1); + ASSERT_NE(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_bits", "a125"), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_md", "SHA1"), 1); + ASSERT_NE(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_md", "sha123"), 1); + + EVP_PKEY *pkey_raw = NULL; + EVP_PKEY_paramgen(ctx.get(), &pkey_raw); + bssl::UniquePtr pkey(pkey_raw); + ASSERT_TRUE(pkey); + + DSA *dsa_raw = EVP_PKEY_get0_DSA(pkey_raw); + ASSERT_TRUE(dsa_raw != NULL); + verify_DSA(dsa_raw, 512 / 8, 160 / 8); + } + + { + bssl::UniquePtr ctx( + EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, nullptr)); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_PKEY_paramgen_init(ctx.get())); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_bits", "768"), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_q_bits", "224"), 1); + ASSERT_NE(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_q_bits", "128"), 1); + ASSERT_NE(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_q_bits", "aghj"), 1); + + EVP_PKEY *pkey_raw = NULL; + EVP_PKEY_paramgen(ctx.get(), &pkey_raw); + bssl::UniquePtr pkey(pkey_raw); + ASSERT_TRUE(pkey); + + DSA *dsa_raw = EVP_PKEY_get0_DSA(pkey_raw); + ASSERT_TRUE(dsa_raw != NULL); + verify_DSA(dsa_raw, 768 / 8, 224 / 8); + } + + { + bssl::UniquePtr ctx( + EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, nullptr)); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_PKEY_paramgen_init(ctx.get())); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_bits", "512"), 1); + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_q_bits", "160"), 1); + // MD takes precedence over qbits + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dsa_paramgen_md", "SHA256"), 1); + + EVP_PKEY *pkey_raw = NULL; + EVP_PKEY_paramgen(ctx.get(), &pkey_raw); + bssl::UniquePtr pkey(pkey_raw); + ASSERT_TRUE(pkey); + + DSA *dsa_raw = EVP_PKEY_get0_DSA(pkey_raw); + ASSERT_TRUE(dsa_raw != NULL); + verify_DSA(dsa_raw, 512 / 8, 256 / 8); + } +} diff --git a/crypto/fipsmodule/evp/internal.h b/crypto/fipsmodule/evp/internal.h index 4891bffdfa..1bc164c30f 100644 --- a/crypto/fipsmodule/evp/internal.h +++ b/crypto/fipsmodule/evp/internal.h @@ -244,6 +244,9 @@ int EVP_RSA_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int optype, int cmd, int p1, void * #define EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN (EVP_PKEY_ALG_CTRL + 20) #define EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR (EVP_PKEY_ALG_CTRL + 21) #define EVP_PKEY_CTRL_SET_MAC_KEY (EVP_PKEY_ALG_CTRL + 22) +#define EVP_PKEY_CTRL_DSA_PARAMGEN_BITS (EVP_PKEY_ALG_CTRL + 23) +#define EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS (EVP_PKEY_ALG_CTRL + 24) +#define EVP_PKEY_CTRL_DSA_PARAMGEN_MD (EVP_PKEY_ALG_CTRL + 25) // EVP_PKEY_CTX_KEYGEN_INFO_COUNT is the maximum array length for // |EVP_PKEY_CTX->keygen_info|. The array length corresponds to the number of @@ -381,10 +384,10 @@ void evp_pkey_set_cb_translate(BN_GENCB *cb, EVP_PKEY_CTX *ctx); #define FIPS_EVP_PKEY_METHODS 7 #ifdef ENABLE_DILITHIUM -#define NON_FIPS_EVP_PKEY_METHODS 3 +#define NON_FIPS_EVP_PKEY_METHODS 4 #define ASN1_EVP_PKEY_METHODS 10 #else -#define NON_FIPS_EVP_PKEY_METHODS 2 +#define NON_FIPS_EVP_PKEY_METHODS 3 #define ASN1_EVP_PKEY_METHODS 9 #endif diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 93d631c7e5..4ce750cbcb 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -1289,19 +1289,28 @@ OPENSSL_EXPORT void OpenSSL_add_all_digests(void); OPENSSL_EXPORT OPENSSL_DEPRECATED void EVP_cleanup(void); -// EVP_PKEY_DSA No-ops [Deprecated]. +// EVP_PKEY_DSA // -// |EVP_PKEY_DSA| is deprecated. It is currently still possible to parse DER -// into a DSA |EVP_PKEY|, but signing or verifying with those objects will not -// work. +// |EVP_PKEY_DSA| is deprecated, but signing or verifying are still supported, +// as is parsing DER into a DSA |EVP_PKEY|. #define EVP_PKEY_DSA NID_dsa -// EVP_PKEY_CTX_set_dsa_paramgen_bits returns zero. +// EVP_PKEY_CTX_set_dsa_paramgen_bits sets the number of bits for DSA paramgen. +// |nbits| must be at least 512. Returns 1 on success, 0 otherwise. OPENSSL_EXPORT OPENSSL_DEPRECATED int EVP_PKEY_CTX_set_dsa_paramgen_bits( EVP_PKEY_CTX *ctx, int nbits); -// EVP_PKEY_CTX_set_dsa_paramgen_q_bits returns zero. +// EVP_PKEY_CTX_set_dsa_paramgen_md sets the digest function used for DSA +// parameter generation. If not specified, one of SHA-1 (160), SHA-224 (224), +// or SHA-256 (256) is selected based on the number of bits in |q|. +OPENSSL_EXPORT OPENSSL_DEPRECATED int EVP_PKEY_CTX_set_dsa_paramgen_md(EVP_PKEY_CTX *ctx, const EVP_MD* md); + +// EVP_PKEY_CTX_set_dsa_paramgen_q_bits sets the number of bits in q to use for +// DSA parameter generation. If not specified, the default is 256. If a digest +// function is specified with |EVP_PKEY_CTX_set_dsa_paramgen_md| then this +// parameter is ignored and the number of bits in q matches the size of the +// digest. This function only accepts the values 160, 224 or 256 for |qbits|. OPENSSL_EXPORT OPENSSL_DEPRECATED int EVP_PKEY_CTX_set_dsa_paramgen_q_bits( EVP_PKEY_CTX *ctx, int qbits);