// Copyright 2006-2019 The OpenSSL Project Authors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 #include #include #include #include #include #include #include "internal.h" 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) { DH_PKEY_CTX *dctx = OPENSSL_zalloc(sizeof(DH_PKEY_CTX)); if (dctx == NULL) { return 0; } // Default parameters dctx->prime_len = 2048; dctx->generator = DH_GENERATOR_2; ctx->data = dctx; return 1; } static int pkey_dh_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) { if (!pkey_dh_init(dst)) { return 0; } const DH_PKEY_CTX *sctx = src->data; DH_PKEY_CTX *dctx = dst->data; dctx->pad = sctx->pad; return 1; } static void pkey_dh_cleanup(EVP_PKEY_CTX *ctx) { OPENSSL_free(ctx->data); ctx->data = NULL; } static int pkey_dh_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) { DH *dh = DH_new(); if (dh == NULL || !EVP_PKEY_assign_DH(pkey, dh)) { DH_free(dh); return 0; } if (ctx->pkey != NULL && !EVP_PKEY_copy_parameters(pkey, ctx->pkey)) { return 0; } return DH_generate_key(dh); } static int pkey_dh_derive(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *out_len) { DH_PKEY_CTX *dctx = ctx->data; if (ctx->pkey == NULL || ctx->peerkey == NULL) { OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET); return 0; } DH *our_key = ctx->pkey->pkey.dh; DH *peer_key = ctx->peerkey->pkey.dh; if (our_key == NULL || peer_key == NULL) { OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET); return 0; } const BIGNUM *pub_key = DH_get0_pub_key(peer_key); if (pub_key == NULL) { OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET); return 0; } if (out == NULL) { *out_len = DH_size(our_key); return 1; } if (*out_len < (size_t)DH_size(our_key)) { OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL); return 0; } int ret = dctx->pad ? DH_compute_key_padded(out, pub_key, our_key) : DH_compute_key(out, pub_key, our_key); if (ret < 0) { return 0; } assert(ret <= DH_size(our_key)); *out_len = (size_t)ret; return 1; } 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: // |EVP_PKEY_derive_set_peer| requires the key implement this command, // even if it is a no-op. return 1; case EVP_PKEY_CTRL_DH_PAD: 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) { int ret = 0; DH_PKEY_CTX *dctx = ctx->data; DH *dh = DH_new(); if (dh == NULL) { return 0; } BN_GENCB *pkey_ctx_cb = NULL; 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); } ret = DH_generate_parameters_ex(dh, dctx->prime_len, dctx->generator, pkey_ctx_cb); end: if (ret == 1) { EVP_PKEY_assign_DH(pkey, dh); } else { ret = 0; DH_free(dh); } BN_GENCB_free(pkey_ctx_cb); return ret; } static int pkey_dh_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, const char *value) { // We don't support: // * dh_rfc5114 // * dh_param // * 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; long pad = strtol(value, &str_end, 10); if(str_end == value || pad < 0 || pad > INT_MAX) { OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_OPERATION); return 0; } return EVP_PKEY_CTX_set_dh_pad(ctx, (int)pad); } return -2; } const EVP_PKEY_METHOD dh_pkey_meth = { .pkey_id = EVP_PKEY_DH, .init = pkey_dh_init, .copy = pkey_dh_copy, .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 }; 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); }