diff --git a/crypto/evp_extra/evp_extra_test.cc b/crypto/evp_extra/evp_extra_test.cc index 899b0d2a28..5502783776 100644 --- a/crypto/evp_extra/evp_extra_test.cc +++ b/crypto/evp_extra/evp_extra_test.cc @@ -1792,6 +1792,50 @@ TEST(EVPExtraTest, DHKeygen) { } } +TEST(EVPExtraTest, DHParamgen) { + std::vector> test_data( + {{768, 3}, {512, DH_GENERATOR_2}, {256, DH_GENERATOR_5}}); + + for (std::pair plgen : test_data) { + const int prime_len = plgen.first; + const int generator = plgen.second; + // Construct a EVP_PKEY_CTX + bssl::UniquePtr ctx( + EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr)); + ASSERT_TRUE(ctx); + // Initialize for paramgen + ASSERT_TRUE(EVP_PKEY_paramgen_init(ctx.get())); + // Set the prime length + ASSERT_TRUE(EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx.get(), prime_len)); + // Set the generator + ASSERT_TRUE(EVP_PKEY_CTX_set_dh_paramgen_generator(ctx.get(), generator)); + + EVP_PKEY *raw_pkey = NULL; + // Generate the parameters + ASSERT_TRUE(EVP_PKEY_paramgen(ctx.get(), &raw_pkey)); + bssl::UniquePtr pkey(raw_pkey); + ASSERT_TRUE(raw_pkey); + + const DH* dh = EVP_PKEY_get0_DH(pkey.get()); + const BIGNUM* p = DH_get0_p(dh); + unsigned p_size = BN_num_bits(p); + ASSERT_EQ(p_size, (unsigned)prime_len); + } + + // Test error conditions + const int prime_len = 255; + const int generator = 1; + // Construct a EVP_PKEY_CTX + bssl::UniquePtr ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, 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_dh_paramgen_prime_len(ctx.get(), prime_len), 1); + // Set the generator + ASSERT_NE(EVP_PKEY_CTX_set_dh_paramgen_generator(ctx.get(), generator), 1); +} + // Test that |EVP_PKEY_keygen| works for Ed25519. TEST(EVPExtraTest, Ed25519Keygen) { bssl::UniquePtr pctx( diff --git a/crypto/evp_extra/p_dh.c b/crypto/evp_extra/p_dh.c index 2ae75f9293..71d7c10e41 100644 --- a/crypto/evp_extra/p_dh.c +++ b/crypto/evp_extra/p_dh.c @@ -21,6 +21,9 @@ typedef struct dh_pkey_ctx_st { int pad; + /* Parameter gen parameters */ + int prime_len; + int generator; } DH_PKEY_CTX; static int pkey_dh_init(EVP_PKEY_CTX *ctx) { @@ -28,6 +31,9 @@ static int pkey_dh_init(EVP_PKEY_CTX *ctx) { if (dctx == NULL) { return 0; } + // Default parameters + dctx->prime_len = 2048; + dctx->generator = DH_GENERATOR_2; ctx->data = dctx; return 1; @@ -104,7 +110,7 @@ static int pkey_dh_derive(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *out_len) { return 1; } -static int pkey_dh_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) { +static int pkey_dh_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *_p2) { DH_PKEY_CTX *dctx = ctx->data; switch (type) { case EVP_PKEY_CTRL_PEER_KEY: @@ -116,21 +122,70 @@ static int pkey_dh_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) { dctx->pad = p1; return 1; + case EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN: + if(p1 < 256) { + return -2; + } + dctx->prime_len = p1; + return 1; + + case EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR: + if(p1 < 2) { + return -2; + } + dctx->generator = p1; + return 1; + default: OPENSSL_PUT_ERROR(EVP, EVP_R_COMMAND_NOT_SUPPORTED); return 0; } } +static int pkey_dh_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) { + DH_PKEY_CTX *dctx = ctx->data; + DH *dh = DH_new(); + if (dh == NULL) { + return 0; + } + int ret = DH_generate_parameters_ex(dh, dctx->prime_len, dctx->generator, NULL); + if (ret == 1) { + EVP_PKEY_assign_DH(pkey, dh); + } else { + ret = 0; + DH_free(dh); + } + return ret; +} + + static int pkey_dh_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, const char *value) { // We don't support: - // * dh_paramgen_prime_len // * dh_rfc5114 // * dh_param - // * dh_paramgen_generator // * dh_paramgen_subprime_len // * dh_paramgen_type + if (strcmp(type, "dh_paramgen_prime_len") == 0) { + char* str_end = NULL; + long prime_len = strtol(value, &str_end, 10); + if(str_end == value || prime_len < 0 || prime_len > INT_MAX) { + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_OPERATION); + return 0; + } + return EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, (int)prime_len); + } + + if (strcmp(type, "dh_paramgen_generator") == 0) { + char* str_end = NULL; + long generator = strtol(value, &str_end, 10); + if(str_end == value || generator < 0 || generator > INT_MAX) { + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_OPERATION); + return 0; + } + return EVP_PKEY_CTX_set_dh_paramgen_generator(ctx, (int)generator); + } + if (strcmp(type, "dh_pad") == 0) { char* str_end = NULL; @@ -139,7 +194,7 @@ static int pkey_dh_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_OPERATION); return 0; } - return EVP_PKEY_CTX_set_dh_pad(ctx, pad); + return EVP_PKEY_CTX_set_dh_pad(ctx, (int)pad); } return -2; } @@ -152,6 +207,7 @@ const EVP_PKEY_METHOD dh_pkey_meth = { .cleanup = pkey_dh_cleanup, .keygen = pkey_dh_keygen, .derive = pkey_dh_derive, + .paramgen = pkey_dh_paramgen, .ctrl = pkey_dh_ctrl, .ctrl_str = pkey_dh_ctrl_str }; @@ -160,3 +216,12 @@ int EVP_PKEY_CTX_set_dh_pad(EVP_PKEY_CTX *ctx, int pad) { return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_DERIVE, EVP_PKEY_CTRL_DH_PAD, pad, NULL); } + +int EVP_PKEY_CTX_set_dh_paramgen_prime_len(EVP_PKEY_CTX *ctx, int pbits) { + return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN, + EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN, pbits, NULL); +} +int EVP_PKEY_CTX_set_dh_paramgen_generator(EVP_PKEY_CTX *ctx, int gen) { + return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN, + EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR, gen, NULL); +} diff --git a/crypto/fipsmodule/evp/evp_ctx_test.cc b/crypto/fipsmodule/evp/evp_ctx_test.cc index 7c54416674..1889b5d56b 100644 --- a/crypto/fipsmodule/evp/evp_ctx_test.cc +++ b/crypto/fipsmodule/evp/evp_ctx_test.cc @@ -4,6 +4,7 @@ #include "internal.h" #include +#include #include #include #include @@ -239,6 +240,31 @@ TEST_F(EvpPkeyCtxCtrlStrTest, DhPad) { // There is no function to retrieve the DH pad value. } +TEST_F(EvpPkeyCtxCtrlStrTest, DhParamGen) { + // Create a EVP_PKEY_CTX with a newly generated DH + bssl::UniquePtr ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr)); + ASSERT_TRUE(ctx); + ASSERT_TRUE(EVP_PKEY_paramgen_init(ctx.get())); + + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dh_paramgen_prime_len", "256"), 1); + ASSERT_NE(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dh_paramgen_prime_len", "gg"), 1); + ASSERT_NE(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dh_paramgen_prime_len", "255"), 1); + + ASSERT_EQ(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dh_paramgen_generator", "5"), 1); + ASSERT_NE(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dh_paramgen_prime_len", "gg"), 1); + ASSERT_NE(EVP_PKEY_CTX_ctrl_str(ctx.get(), "dh_paramgen_prime_len", "1"), 1); + + EVP_PKEY* raw = nullptr; + ASSERT_EQ(EVP_PKEY_paramgen(ctx.get(), &raw), 1); + bssl::UniquePtr pkey(raw); + ASSERT_TRUE(raw); + + const DH* dh = EVP_PKEY_get0_DH(pkey.get()); + const BIGNUM* p = DH_get0_p(dh); + unsigned p_size = BN_num_bits(p); + ASSERT_EQ(p_size, 256u); +} + static const char *hkdf_hexsalt = "000102030405060708090a0b0c"; static const char *hkdf_hexinfo = "f0f1f2f3f4f5f6f7f8f9"; static const char *hkdf_hexkey = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; diff --git a/crypto/fipsmodule/evp/internal.h b/crypto/fipsmodule/evp/internal.h index adc76556e6..847aee09df 100644 --- a/crypto/fipsmodule/evp/internal.h +++ b/crypto/fipsmodule/evp/internal.h @@ -241,6 +241,8 @@ int EVP_RSA_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int optype, int cmd, int p1, void * #define EVP_PKEY_CTRL_HKDF_SALT (EVP_PKEY_ALG_CTRL + 17) #define EVP_PKEY_CTRL_HKDF_INFO (EVP_PKEY_ALG_CTRL + 18) #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) // 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 diff --git a/include/openssl/evp.h b/include/openssl/evp.h index ea18ee2d8b..93d631c7e5 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -181,6 +181,18 @@ OPENSSL_EXPORT int EVP_PKEY_assign_DH(EVP_PKEY *pkey, DH *key); OPENSSL_EXPORT DH *EVP_PKEY_get0_DH(const EVP_PKEY *pkey); OPENSSL_EXPORT DH *EVP_PKEY_get1_DH(const EVP_PKEY *pkey); +// EVP_PKEY_CTX_set_dh_paramgen_prime_len sets the length of the DH prime +// parameter p for DH parameter generation. If this function is not called, +// the default length of 2048 is used. |pbits| must be greater than or equal +// to 256. Returns 1 on success, otherwise returns a non-positive value. +OPENSSL_EXPORT int EVP_PKEY_CTX_set_dh_paramgen_prime_len(EVP_PKEY_CTX *ctx, int pbits); + +// EVP_PKEY_CTX_set_dh_paramgen_generator sets the DH generator for DH parameter +// generation. If this function is not called, the default value of 2 is used. +// |gen| must be greater than 1. Returns 1 on success, otherwise returns a +// non-positive value. +OPENSSL_EXPORT int EVP_PKEY_CTX_set_dh_paramgen_generator(EVP_PKEY_CTX *ctx, int gen); + #define EVP_PKEY_NONE NID_undef #define EVP_PKEY_RSA NID_rsaEncryption #define EVP_PKEY_RSA_PSS NID_rsassaPss