From c17adaa699d2f75640445283c82dbaad33725fce Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Mon, 28 Oct 2024 12:44:22 -0400 Subject: [PATCH] Ruby Support - More EVP_PKEY_DSA --- crypto/CMakeLists.txt | 1 + crypto/evp_extra/internal.h | 1 + crypto/evp_extra/p_dsa.c | 343 +++++++++++++++++++++++++++++++ crypto/evp_extra/p_dsa_asn1.c | 12 -- crypto/evp_extra/p_methods.c | 1 + crypto/fipsmodule/evp/internal.h | 10 +- include/openssl/evp.h | 25 ++- 7 files changed, 371 insertions(+), 22 deletions(-) create mode 100644 crypto/evp_extra/p_dsa.c diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index b2314d263c..6e2e32cd5c 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/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..a27b165c20 --- /dev/null +++ b/crypto/evp_extra/p_dsa.c @@ -0,0 +1,343 @@ +// 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 2048A + 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); + return 0; + } + } + + if (!((dsa = DSA_new()))) { + goto end; + } + + const size_t qsize = EVP_MD_size(pmd); + if (!dsa_internal_paramgen(dsa, dctx->nbits, qsize, 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); + + 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; + } + + if (dctx->md != NULL && tbslen != EVP_MD_size(dctx->md)) { + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PARAMETERS); + return 0; + } + + DSA_SIG *result = DSA_do_sign(tbs, tbslen, dsa); + if (result == NULL) { + return 0; + } + CBB sig_bytes; + if (1 != CBB_init(&sig_bytes, tbslen)) { + return 0; + } + DSA_SIG_marshal(&sig_bytes, result); + uint8_t *sig_buffer = NULL; + if (1 != CBB_finish(&sig_bytes, &sig_buffer, siglen)) { + return 0; + } + OPENSSL_memcpy(sig, sig_buffer, *siglen); + OPENSSL_free(sig_buffer); + return 1; +} + +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; + } + + CBS sig_cbs; + CBS_init(&sig_cbs, sig, siglen); + DSA_SIG *dsa_sig = DSA_SIG_parse(&sig_cbs); + if (dsa_sig == NULL) { + return 0; + } + if (1 != DSA_do_verify(tbs, tbslen, dsa_sig, dsa)) { + return 0; + } + + return 1; +} + +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 < 256) + 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_dsa: + case NID_dsaWithSHA: + 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: + *(const EVP_MD **)p2 = dctx->md; + return 1; + + case EVP_PKEY_CTRL_DIGESTINIT: + case EVP_PKEY_CTRL_PKCS7_SIGN: + case EVP_PKEY_CTRL_CMS_SIGN: + return 1; + + case EVP_PKEY_CTRL_PEER_KEY: + OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + return -2; + default: + return -2; + } +} + +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; + } + return EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx, (int)nbits); + } + 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; + } + return EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx, (int)qbits); + } + 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; + } + return EVP_PKEY_CTX_set_dsa_paramgen_md(ctx, md); + } + 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..5c5be2a7e7 100644 --- a/crypto/evp_extra/p_methods.c +++ b/crypto/evp_extra/p_methods.c @@ -13,6 +13,7 @@ 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. diff --git a/crypto/fipsmodule/evp/internal.h b/crypto/fipsmodule/evp/internal.h index f90c6ff6c7..107950c1da 100644 --- a/crypto/fipsmodule/evp/internal.h +++ b/crypto/fipsmodule/evp/internal.h @@ -243,6 +243,12 @@ int EVP_RSA_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int optype, int cmd, int p1, void * #define EVP_PKEY_CTRL_DH_PAD (EVP_PKEY_ALG_CTRL + 19) #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_DSA_PARAMGEN_BITS (EVP_PKEY_ALG_CTRL + 22) +#define EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS (EVP_PKEY_ALG_CTRL + 23) +#define EVP_PKEY_CTRL_DSA_PARAMGEN_MD (EVP_PKEY_ALG_CTRL + 24) +#define EVP_PKEY_CTRL_DIGESTINIT (EVP_PKEY_ALG_CTRL + 25) +#define EVP_PKEY_CTRL_PKCS7_SIGN (EVP_PKEY_ALG_CTRL + 26) +#define EVP_PKEY_CTRL_CMS_SIGN (EVP_PKEY_ALG_CTRL + 27) // 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 @@ -374,10 +380,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..7f3c75a4ea 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -1289,20 +1289,29 @@ 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. -OPENSSL_EXPORT OPENSSL_DEPRECATED int EVP_PKEY_CTX_set_dsa_paramgen_bits( +// EVP_PKEY_CTX_set_dsa_paramgen_bits sets the number of bits for DSA paramgen. +// |nbits| must be larger than 256. Returns 1 on success, 0 otherwise. +OPENSSL_EXPORT int EVP_PKEY_CTX_set_dsa_paramgen_bits( EVP_PKEY_CTX *ctx, int nbits); -// EVP_PKEY_CTX_set_dsa_paramgen_q_bits returns zero. -OPENSSL_EXPORT OPENSSL_DEPRECATED int EVP_PKEY_CTX_set_dsa_paramgen_q_bits( +// EVP_PKEY_CTX_set_dsa_paramgen_md sets the digest function used for DSA +// parameter generation. If not specified, one of SHA-1, SHA-224, or SHA-256 is +// selected on the number of bits in |q|. +OPENSSL_EXPORT 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. +OPENSSL_EXPORT int EVP_PKEY_CTX_set_dsa_paramgen_q_bits( EVP_PKEY_CTX *ctx, int qbits);