Commit bd59f2b9 authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

CMS RFC2631 X9.42 DH enveloped data support.

parent dc1ce3bc
Loading
Loading
Loading
Loading
+69 −0
Original line number Diff line number Diff line
@@ -270,11 +270,74 @@ int DH_KDF_X9_42(unsigned char *out, size_t outlen,
	EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_PARAMGEN, \
			EVP_PKEY_CTRL_DH_RFC5114, gen, NULL)

#define EVP_PKEY_CTX_set_dh_kdf_type(ctx, kdf) \
	EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, \
				EVP_PKEY_OP_DERIVE, \
				EVP_PKEY_CTRL_DH_KDF_TYPE, kdf, NULL)

#define EVP_PKEY_CTX_get_dh_kdf_type(ctx) \
	EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, \
				EVP_PKEY_OP_DERIVE, \
				EVP_PKEY_CTRL_DH_KDF_TYPE, -2, NULL)

#define EVP_PKEY_CTX_set0_dh_kdf_oid(ctx, oid) \
	EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, \
				EVP_PKEY_OP_DERIVE, \
				EVP_PKEY_CTRL_DH_KDF_OID, 0, (void *)oid)

#define EVP_PKEY_CTX_get0_dh_kdf_oid(ctx, poid) \
	EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, \
				EVP_PKEY_OP_DERIVE, \
				EVP_PKEY_CTRL_GET_DH_KDF_OID, 0, (void *)poid)

#define EVP_PKEY_CTX_set_dh_kdf_md(ctx, md) \
	EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, \
				EVP_PKEY_OP_DERIVE, \
				EVP_PKEY_CTRL_DH_KDF_MD, 0, (void *)md)

#define EVP_PKEY_CTX_get_dh_kdf_md(ctx, pmd) \
	EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, \
				EVP_PKEY_OP_DERIVE, \
				EVP_PKEY_CTRL_GET_DH_KDF_MD, 0, (void *)pmd)

#define EVP_PKEY_CTX_set_dh_kdf_outlen(ctx, len) \
	EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, \
				EVP_PKEY_OP_DERIVE, \
				EVP_PKEY_CTRL_DH_KDF_OUTLEN, len, NULL)

#define EVP_PKEY_CTX_get_dh_kdf_outlen(ctx, plen) \
	EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, \
				EVP_PKEY_OP_DERIVE, \
			EVP_PKEY_CTRL_GET_DH_KDF_OUTLEN, 0, (void *)plen)

#define EVP_PKEY_CTX_set0_dh_kdf_ukm(ctx, p, plen) \
	EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, \
				EVP_PKEY_OP_DERIVE, \
				EVP_PKEY_CTRL_DH_KDF_UKM, plen, (void *)p)

#define EVP_PKEY_CTX_get0_dh_kdf_ukm(ctx, p) \
	EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, \
				EVP_PKEY_OP_DERIVE, \
				EVP_PKEY_CTRL_GET_DH_KDF_UKM, 0, (void *)p)

#define	EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN	(EVP_PKEY_ALG_CTRL + 1)
#define	EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR	(EVP_PKEY_ALG_CTRL + 2)
#define	EVP_PKEY_CTRL_DH_RFC5114		(EVP_PKEY_ALG_CTRL + 3)
#define	EVP_PKEY_CTRL_DH_PARAMGEN_SUBPRIME_LEN	(EVP_PKEY_ALG_CTRL + 4)
#define	EVP_PKEY_CTRL_DH_PARAMGEN_TYPE		(EVP_PKEY_ALG_CTRL + 5)
#define EVP_PKEY_CTRL_DH_KDF_TYPE		(EVP_PKEY_ALG_CTRL + 6)
#define EVP_PKEY_CTRL_DH_KDF_MD			(EVP_PKEY_ALG_CTRL + 7)
#define EVP_PKEY_CTRL_GET_DH_KDF_MD		(EVP_PKEY_ALG_CTRL + 8)
#define EVP_PKEY_CTRL_DH_KDF_OUTLEN		(EVP_PKEY_ALG_CTRL + 9)
#define EVP_PKEY_CTRL_GET_DH_KDF_OUTLEN		(EVP_PKEY_ALG_CTRL + 10)
#define EVP_PKEY_CTRL_DH_KDF_UKM		(EVP_PKEY_ALG_CTRL + 11)
#define EVP_PKEY_CTRL_GET_DH_KDF_UKM		(EVP_PKEY_ALG_CTRL + 12)
#define EVP_PKEY_CTRL_DH_KDF_OID		(EVP_PKEY_ALG_CTRL + 13)
#define EVP_PKEY_CTRL_GET_DH_KDF_OID		(EVP_PKEY_ALG_CTRL + 14)

/* KDF types */
#define EVP_PKEY_DH_KDF_NONE				1
#define EVP_PKEY_DH_KDF_X9_42				2

/* BEGIN ERROR CODES */
/* The following lines are auto generated by the script mkerr.pl. Any changes
@@ -288,6 +351,9 @@ void ERR_load_DH_strings(void);
#define DH_F_COMPUTE_KEY				 102
#define DH_F_DHPARAMS_PRINT_FP				 101
#define DH_F_DH_BUILTIN_GENPARAMS			 106
#define DH_F_DH_CMS_DECRYPT				 114
#define DH_F_DH_CMS_SET_PEERKEY				 115
#define DH_F_DH_CMS_SET_SHARED_INFO			 116
#define DH_F_DH_NEW_METHOD				 105
#define DH_F_DH_PARAM_DECODE				 107
#define DH_F_DH_PRIV_DECODE				 110
@@ -306,12 +372,15 @@ void ERR_load_DH_strings(void);
#define DH_R_BN_ERROR					 106
#define DH_R_DECODE_ERROR				 104
#define DH_R_INVALID_PUBKEY				 102
#define DH_R_KDF_PARAMETER_ERROR			 112
#define DH_R_KEYS_NOT_SET				 108
#define DH_R_KEY_SIZE_TOO_SMALL				 110
#define DH_R_MODULUS_TOO_LARGE				 103
#define DH_R_NO_PARAMETERS_SET				 107
#define DH_R_NO_PRIVATE_VALUE				 100
#define DH_R_PARAMETER_ENCODING_ERROR			 105
#define DH_R_PEER_KEY_ERROR				 111
#define DH_R_SHARED_INFO_ERROR				 113

#ifdef  __cplusplus
}
+349 −1
Original line number Diff line number Diff line
@@ -62,6 +62,9 @@
#include <openssl/dh.h>
#include <openssl/bn.h>
#include "asn1_locl.h"
#ifndef OPENSSL_NO_CMS
#include <openssl/cms.h>
#endif

extern const EVP_PKEY_ASN1_METHOD dhx_asn1_meth;

@@ -569,6 +572,34 @@ int DHparams_print(BIO *bp, const DH *x)
	return do_dh_print(bp, x, 4, NULL, 0);
	}

#ifndef OPENSSL_NO_CMS
static int dh_cms_decrypt(CMS_RecipientInfo *ri);
static int dh_cms_encrypt(CMS_RecipientInfo *ri);
#endif

static int dh_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
	{
	switch (op)
		{
#ifndef OPENSSL_NO_CMS

		case ASN1_PKEY_CTRL_CMS_ENVELOPE:
		if (arg1 == 1)
			return dh_cms_decrypt(arg2);
		else if (arg1 == 0)
			return dh_cms_encrypt(arg2);
		return -2;

		case ASN1_PKEY_CTRL_CMS_RI_TYPE:
		*(int *)arg2 = CMS_RECIPINFO_AGREE;
		return 1;
#endif
		default:
		return -2;
		}

	}

const EVP_PKEY_ASN1_METHOD dh_asn1_meth = 
	{
	EVP_PKEY_DH,
@@ -632,6 +663,323 @@ const EVP_PKEY_ASN1_METHOD dhx_asn1_meth =
	0,

	int_dh_free,
	0
	dh_pkey_ctrl
	};
#ifndef OPENSSL_NO_CMS

static int dh_cms_set_peerkey(EVP_PKEY_CTX *pctx,
				X509_ALGOR *alg, ASN1_BIT_STRING *pubkey)
	{
	ASN1_OBJECT *aoid;
	int atype;
	void *aval;
	ASN1_INTEGER *public_key;
	int rv = 0;
	EVP_PKEY *pkpeer = NULL, *pk = NULL;
	DH *dhpeer = NULL;
	const unsigned char *p;
	int plen;
	X509_ALGOR_get0(&aoid, &atype, &aval, alg);
	if (OBJ_obj2nid(aoid) != NID_dhpublicnumber)
		goto err;
	/* Only absent parameters allowed in RFC XXXX */
	if (atype != V_ASN1_UNDEF && atype == V_ASN1_NULL)
		goto err;

	pk = EVP_PKEY_CTX_get0_pkey(pctx);
	if (!pk)
		goto err;
	if (pk->type != EVP_PKEY_DHX)
		goto err;
	/* Get parameters from parent key */
	dhpeer = DHparams_dup(pk->pkey.dh);
	/* We have parameters now set public key */
	plen = ASN1_STRING_length(pubkey);
	p = ASN1_STRING_data(pubkey);
	if (!p || !plen)
		goto err;

	if (!(public_key=d2i_ASN1_INTEGER(NULL, &p, plen)))
		{
		DHerr(DH_F_DH_CMS_SET_PEERKEY, DH_R_DECODE_ERROR);
		goto err;
		}

	/* We have parameters now set public key */
	if (!(dhpeer->pub_key = ASN1_INTEGER_to_BN(public_key, NULL)))
		{
		DHerr(DH_F_DH_CMS_SET_PEERKEY, DH_R_BN_DECODE_ERROR);
		goto err;
		}

	pkpeer = EVP_PKEY_new();
	if (!pkpeer)
		goto err;
	EVP_PKEY_assign(pkpeer, pk->ameth->pkey_id, dhpeer);
	dhpeer = NULL;
	if (EVP_PKEY_derive_set_peer(pctx, pkpeer) > 0)
		rv = 1;
	err:
	if (public_key)
		ASN1_INTEGER_free(public_key);
	if (pkpeer)
		EVP_PKEY_free(pkpeer);
	if (dhpeer)
		DH_free(dhpeer);
	return rv;
	}

static int dh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri)
	{
	int rv = 0;

	X509_ALGOR *alg, *kekalg = NULL;
	ASN1_OCTET_STRING *ukm;
	const unsigned char *p;
	unsigned char *dukm = NULL;
	size_t dukmlen;
	int keylen, plen;
	const EVP_CIPHER *kekcipher;
	EVP_CIPHER_CTX *kekctx;

	if (!CMS_RecipientInfo_kari_get0_alg(ri, &alg, &ukm))
		goto err;

	/* For DH we only have one OID permissible. If ever any more get
	 * defined we will need something cleverer.
	 */
	if (OBJ_obj2nid(alg->algorithm) != NID_id_smime_alg_ESDH)
		{
		DHerr(DH_F_DH_CMS_SET_SHARED_INFO, DH_R_KDF_PARAMETER_ERROR);
		goto err;
		}

	if (EVP_PKEY_CTX_set_dh_kdf_type(pctx, EVP_PKEY_DH_KDF_X9_42) <= 0)
		goto err;

	if (EVP_PKEY_CTX_set_dh_kdf_md(pctx, EVP_sha1()) <= 0)
		goto err;

	if (alg->parameter->type != V_ASN1_SEQUENCE)
		goto err;

	p = alg->parameter->value.sequence->data;
	plen = alg->parameter->value.sequence->length;
	kekalg = d2i_X509_ALGOR(NULL, &p, plen);
	if (!kekalg)
		goto err;
	kekctx = CMS_RecipientInfo_kari_get0_ctx(ri);
	if (!kekctx)
		goto err;
	kekcipher = EVP_get_cipherbyobj(kekalg->algorithm);
	if (!kekcipher || EVP_CIPHER_mode(kekcipher) != EVP_CIPH_WRAP_MODE)
		goto err;
	if (!EVP_EncryptInit_ex(kekctx, kekcipher, NULL, NULL, NULL))
		goto err;
	if (EVP_CIPHER_asn1_to_param(kekctx, kekalg->parameter) <= 0)
		goto err;

	keylen = EVP_CIPHER_CTX_key_length(kekctx);
	if (EVP_PKEY_CTX_set_dh_kdf_outlen(pctx, keylen) <= 0)
		goto err;
	/* Use OBJ_nid2obj to ensure we use built in OID that isn't freed */
	if (EVP_PKEY_CTX_set0_dh_kdf_oid(pctx,
				OBJ_nid2obj(EVP_CIPHER_type(kekcipher))) <= 0)
		goto err;

	if (ukm)
		{
		dukmlen = ASN1_STRING_length(ukm);
		dukm = BUF_memdup(ASN1_STRING_data(ukm), dukmlen);
		if (!dukm)
			goto err;
		}

	if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, dukmlen) <= 0)
		goto err;
	dukm = NULL;

	rv = 1;
	err:
	if (kekalg)
		X509_ALGOR_free(kekalg);
	if (dukm)
		OPENSSL_free(dukm);
	return rv;
	}

static int dh_cms_decrypt(CMS_RecipientInfo *ri)
	{
	EVP_PKEY_CTX *pctx;
	pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
	if (!pctx)
		return 0;
	/* See if we need to set peer key */
	if (!EVP_PKEY_CTX_get0_peerkey(pctx))
		{
		X509_ALGOR *alg;
		ASN1_BIT_STRING *pubkey;
		if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &alg, &pubkey,
							NULL, NULL, NULL))
			return 0;
		if (!alg || !pubkey)
			return 0;
		if (!dh_cms_set_peerkey(pctx, alg, pubkey))
			{
			DHerr(DH_F_DH_CMS_DECRYPT, DH_R_PEER_KEY_ERROR);
			return 0;
			}
		}
	/* Set DH derivation parameters and initialise unwrap context */
	if (!dh_cms_set_shared_info(pctx, ri))
		{
		DHerr(DH_F_DH_CMS_DECRYPT, DH_R_SHARED_INFO_ERROR);
		return 0;
		}
	return 1;
	}

static int dh_cms_encrypt(CMS_RecipientInfo *ri)
	{
	EVP_PKEY_CTX *pctx;
	EVP_PKEY *pkey;
	EVP_CIPHER_CTX *ctx;
	int keylen;
	X509_ALGOR *talg, *wrap_alg = NULL;
	ASN1_OBJECT *aoid;
	ASN1_BIT_STRING *pubkey;
	ASN1_STRING *wrap_str;
	ASN1_OCTET_STRING *ukm;
	unsigned char *penc = NULL, *dukm = NULL;
	int penclen;
	size_t dukmlen;
	int rv = 0;
	int kdf_type, wrap_nid;
	const EVP_MD *kdf_md;
	pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
	if (!pctx)
		return 0;
	/* Get ephemeral key */
	pkey = EVP_PKEY_CTX_get0_pkey(pctx);
	if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &talg, &pubkey,
							NULL, NULL, NULL))
		goto err;
	X509_ALGOR_get0(&aoid, NULL, NULL, talg);
	/* Is everything uninitialised? */
	if (aoid == OBJ_nid2obj(NID_undef))
		{
		ASN1_INTEGER *pubk;
		pubk = BN_to_ASN1_INTEGER(pkey->pkey.dh->pub_key, NULL);
		if (!pubk)
			goto err;
		/* Set the key */

		penclen = i2d_ASN1_INTEGER(pubk, &penc);
		ASN1_INTEGER_free(pubk);
		if (penclen <= 0)
			goto err;
		ASN1_STRING_set0(pubkey, penc, penclen);
		pubkey->flags&= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07);
		pubkey->flags|=ASN1_STRING_FLAG_BITS_LEFT;

		penc = NULL;
		X509_ALGOR_set0(talg, OBJ_nid2obj(NID_dhpublicnumber),
							V_ASN1_UNDEF, NULL);
		}

	/* See if custom paraneters set */
	kdf_type = EVP_PKEY_CTX_get_dh_kdf_type(pctx);
	if (kdf_type <= 0)
		goto err;
	if (!EVP_PKEY_CTX_get_dh_kdf_md(pctx, &kdf_md))
		goto err;

	if (kdf_type == EVP_PKEY_DH_KDF_NONE)
		{
		kdf_type = EVP_PKEY_DH_KDF_X9_42;
		if (EVP_PKEY_CTX_set_dh_kdf_type(pctx, kdf_type) <= 0)
			goto err;
		}
	else if (kdf_type != EVP_PKEY_DH_KDF_X9_42)
		/* Unknown KDF */
		goto err;
	if (kdf_md == NULL)
		{
		/* Only SHA1 supported */
		kdf_md = EVP_sha1();
		if (EVP_PKEY_CTX_set_dh_kdf_md(pctx, kdf_md) <= 0)
			goto err;
		}
	else if (EVP_MD_type(kdf_md) != NID_sha1)
		/* Unsupported digest */
		goto err;

	if (!CMS_RecipientInfo_kari_get0_alg(ri, &talg, &ukm))
		goto err;

	/* Get wrap NID */
	ctx = CMS_RecipientInfo_kari_get0_ctx(ri);
	wrap_nid = EVP_CIPHER_CTX_type(ctx);
	if (EVP_PKEY_CTX_set0_dh_kdf_oid(pctx, OBJ_nid2obj(wrap_nid)) <= 0)
		goto err;
	keylen = EVP_CIPHER_CTX_key_length(ctx);

	/* Package wrap algorithm in an AlgorithmIdentifier */

	wrap_alg = X509_ALGOR_new();
	if (!wrap_alg)
		goto err;
	wrap_alg->algorithm = OBJ_nid2obj(wrap_nid);
	wrap_alg->parameter = ASN1_TYPE_new();
	if (!wrap_alg->parameter)
		goto err;
	if (EVP_CIPHER_param_to_asn1(ctx, wrap_alg->parameter) <= 0)
		goto err;
	if (ASN1_TYPE_get(wrap_alg->parameter) == NID_undef)
		{
		ASN1_TYPE_free(wrap_alg->parameter);
		wrap_alg->parameter = NULL;
		}

	if (EVP_PKEY_CTX_set_dh_kdf_outlen(pctx, keylen) <= 0)
		goto err;

	if (ukm)
		{
		dukmlen = ASN1_STRING_length(ukm);
		dukm = BUF_memdup(ASN1_STRING_data(ukm), dukmlen);
		if (!dukm)
			goto err;
		}

	if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, dukmlen) <= 0)
		goto err;
	dukm = NULL;

	/* Now need to wrap encoding of wrap AlgorithmIdentifier into
	 * parameter of another AlgorithmIdentifier.
	 */
	penc = NULL;
	penclen = i2d_X509_ALGOR(wrap_alg, &penc);
	if (!penc || !penclen)
		goto err;
	wrap_str = ASN1_STRING_new();
	if (!wrap_str)
		goto err;
	ASN1_STRING_set0(wrap_str, penc, penclen);
	penc = NULL;
	X509_ALGOR_set0(talg, OBJ_nid2obj(NID_id_smime_alg_ESDH),
						V_ASN1_SEQUENCE, wrap_str);

	rv = 1;

	err:
	if (penc)
		OPENSSL_free(penc);
	if (wrap_alg)
		X509_ALGOR_free(wrap_alg);
	return rv;
	}

#endif
+7 −1
Original line number Diff line number Diff line
/* crypto/dh/dh_err.c */
/* ====================================================================
 * Copyright (c) 1999-2010 The OpenSSL Project.  All rights reserved.
 * Copyright (c) 1999-2013 The OpenSSL Project.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
@@ -73,6 +73,9 @@ static ERR_STRING_DATA DH_str_functs[]=
{ERR_FUNC(DH_F_COMPUTE_KEY),	"COMPUTE_KEY"},
{ERR_FUNC(DH_F_DHPARAMS_PRINT_FP),	"DHparams_print_fp"},
{ERR_FUNC(DH_F_DH_BUILTIN_GENPARAMS),	"DH_BUILTIN_GENPARAMS"},
{ERR_FUNC(DH_F_DH_CMS_DECRYPT),	"DH_CMS_DECRYPT"},
{ERR_FUNC(DH_F_DH_CMS_SET_PEERKEY),	"DH_CMS_SET_PEERKEY"},
{ERR_FUNC(DH_F_DH_CMS_SET_SHARED_INFO),	"DH_CMS_SET_SHARED_INFO"},
{ERR_FUNC(DH_F_DH_NEW_METHOD),	"DH_new_method"},
{ERR_FUNC(DH_F_DH_PARAM_DECODE),	"DH_PARAM_DECODE"},
{ERR_FUNC(DH_F_DH_PRIV_DECODE),	"DH_PRIV_DECODE"},
@@ -94,12 +97,15 @@ static ERR_STRING_DATA DH_str_reasons[]=
{ERR_REASON(DH_R_BN_ERROR)               ,"bn error"},
{ERR_REASON(DH_R_DECODE_ERROR)           ,"decode error"},
{ERR_REASON(DH_R_INVALID_PUBKEY)         ,"invalid public key"},
{ERR_REASON(DH_R_KDF_PARAMETER_ERROR)    ,"kdf parameter error"},
{ERR_REASON(DH_R_KEYS_NOT_SET)           ,"keys not set"},
{ERR_REASON(DH_R_KEY_SIZE_TOO_SMALL)     ,"key size too small"},
{ERR_REASON(DH_R_MODULUS_TOO_LARGE)      ,"modulus too large"},
{ERR_REASON(DH_R_NO_PARAMETERS_SET)      ,"no parameters set"},
{ERR_REASON(DH_R_NO_PRIVATE_VALUE)       ,"no private value"},
{ERR_REASON(DH_R_PARAMETER_ENCODING_ERROR),"parameter encoding error"},
{ERR_REASON(DH_R_PEER_KEY_ERROR)         ,"peer key error"},
{ERR_REASON(DH_R_SHARED_INFO_ERROR)      ,"shared info error"},
{0,NULL}
	};

+140 −7
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@
#ifndef OPENSSL_NO_DSA
#include <openssl/dsa.h>
#endif
#include <openssl/objects.h>
#include "evp_locl.h"

/* DH pkey context structure */
@@ -76,11 +77,22 @@ typedef struct
	int generator;
	int use_dsa;
	int subprime_len;
	/* message digest used for parameter generation */
	const EVP_MD *md;
	int rfc5114_param;
	/* Keygen callback info */
	int gentmp[2];
	/* message digest */
	/* KDF (if any) to use for DH */
	char kdf_type;
	/* OID to use for KDF */
	ASN1_OBJECT *kdf_oid;
	/* Message digest to use for key derivation */
	const EVP_MD *kdf_md;
	/* User key material */
	unsigned char *kdf_ukm;
	size_t kdf_ukmlen;
	/* KDF output length */
	size_t kdf_outlen;
	} DH_PKEY_CTX;

static int pkey_dh_init(EVP_PKEY_CTX *ctx)
@@ -96,6 +108,13 @@ static int pkey_dh_init(EVP_PKEY_CTX *ctx)
	dctx->md = NULL;
	dctx->rfc5114_param = 0;

	dctx->kdf_type = EVP_PKEY_DH_KDF_NONE;
	dctx->kdf_oid = NULL;
	dctx->kdf_md = NULL;
	dctx->kdf_ukm = NULL;
	dctx->kdf_ukmlen = 0;
	dctx->kdf_outlen = 0;

	ctx->data = dctx;
	ctx->keygen_info = dctx->gentmp;
	ctx->keygen_info_count = 2;
@@ -116,6 +135,18 @@ static int pkey_dh_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
	dctx->use_dsa = sctx->use_dsa;
	dctx->md = sctx->md;
	dctx->rfc5114_param = sctx->rfc5114_param;

	dctx->kdf_type = sctx->kdf_type;
	dctx->kdf_oid = OBJ_dup(sctx->kdf_oid);
	if (!dctx->kdf_oid)
		return 0;
	dctx->kdf_md = sctx->kdf_md;
	if (dctx->kdf_ukm)
		{
		dctx->kdf_ukm = BUF_memdup(sctx->kdf_ukm, sctx->kdf_ukmlen);
		dctx->kdf_ukmlen = sctx->kdf_ukmlen;
		}
	dctx->kdf_outlen = sctx->kdf_outlen;
	return 1;
	}

@@ -123,8 +154,14 @@ static void pkey_dh_cleanup(EVP_PKEY_CTX *ctx)
	{
	DH_PKEY_CTX *dctx = ctx->data;
	if (dctx)
		{
		if (dctx->kdf_ukm)
			OPENSSL_free(dctx->kdf_ukm);
		if (dctx->kdf_oid)
			ASN1_OBJECT_free(dctx->kdf_oid);
		OPENSSL_free(dctx);
		}
	}

static int pkey_dh_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
	{
@@ -170,6 +207,57 @@ static int pkey_dh_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
		/* Default behaviour is OK */
		return 1;

		case EVP_PKEY_CTRL_DH_KDF_TYPE:
		if (p1 == -2)
			return dctx->kdf_type;
		if (p1 != EVP_PKEY_DH_KDF_NONE &&
		    p1 != EVP_PKEY_DH_KDF_X9_42)
			return -2;
		dctx->kdf_type = p1;
		return 1;

		case EVP_PKEY_CTRL_DH_KDF_MD:
		dctx->kdf_md = p2;
		return 1;

		case EVP_PKEY_CTRL_GET_DH_KDF_MD:
		*(const EVP_MD **)p2 = dctx->kdf_md;
		return 1;

		case EVP_PKEY_CTRL_DH_KDF_OUTLEN:
		if (p1 <= 0)
			return -2;
		dctx->kdf_outlen = (size_t)p1;
		return 1;

		case EVP_PKEY_CTRL_GET_DH_KDF_OUTLEN:
		*(int *)p2 = dctx->kdf_outlen;
		return 1;

		case EVP_PKEY_CTRL_DH_KDF_UKM:
		if (dctx->kdf_ukm)
			OPENSSL_free(dctx->kdf_ukm);
		dctx->kdf_ukm = p2;
		if (p2)
			dctx->kdf_ukmlen = p1;
		else
			dctx->kdf_ukmlen = 0;
		return 1;

		case EVP_PKEY_CTRL_GET_DH_KDF_UKM:
		*(unsigned char **)p2 = dctx->kdf_ukm;
		return dctx->kdf_ukmlen;

		case EVP_PKEY_CTRL_DH_KDF_OID:
		if (dctx->kdf_oid)
			ASN1_OBJECT_free(dctx->kdf_oid);
		dctx->kdf_oid = p2;
		return 1;

		case EVP_PKEY_CTRL_GET_DH_KDF_OID:
		*(ASN1_OBJECT **)p2 = dctx->kdf_oid;
		return 1;

		default:
		return -2;

@@ -356,23 +444,68 @@ static int pkey_dh_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
static int pkey_dh_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen)
	{
	int ret;
	DH *dh;
	DH_PKEY_CTX *dctx = ctx->data;
	BIGNUM *dhpub;
	if (!ctx->pkey || !ctx->peerkey)
		{
		DHerr(DH_F_PKEY_DH_DERIVE, DH_R_KEYS_NOT_SET);
		return 0;
		}
	ret = DH_compute_key(key, ctx->peerkey->pkey.dh->pub_key,
							ctx->pkey->pkey.dh);
	dh = ctx->pkey->pkey.dh;
	dhpub = ctx->peerkey->pkey.dh->pub_key;
	if (dctx->kdf_type == EVP_PKEY_DH_KDF_NONE)
		{
		if (key == NULL)
			{
			*keylen = DH_size(dh);
			return 1;
			}
		ret = DH_compute_key(key, dhpub, dh);
		if (ret < 0)
			return ret;
		*keylen = ret;
		return 1;
		}
	else if (dctx->kdf_type == EVP_PKEY_DH_KDF_X9_42)
		{
		unsigned char *Z = NULL;
		size_t Zlen = 0;
		if (!dctx->kdf_outlen || !dctx->kdf_oid)
			return 0;
		if (key == NULL)
			{
			*keylen = dctx->kdf_outlen;
			return 1;
			}
		if (*keylen != dctx->kdf_outlen)
			return 0;
		ret = 0;
		Zlen = DH_size(dh);
		Z = OPENSSL_malloc(Zlen);
		if (DH_compute_key_padded(Z, dhpub, dh) <= 0)
			goto err;
		if (!DH_KDF_X9_42(key, *keylen, Z, Zlen, dctx->kdf_oid,
					dctx->kdf_ukm, dctx->kdf_ukmlen,
					dctx->kdf_md))
			goto err;
		*keylen = dctx->kdf_outlen;
		ret = 1;
		err:
		if (Z)
			{
			OPENSSL_cleanse(Z, Zlen);
			OPENSSL_free(Z);
			}
		return ret;
		}
	return 1;
	}

const EVP_PKEY_METHOD dh_pkey_meth = 
	{
	EVP_PKEY_DH,
	EVP_PKEY_FLAG_AUTOARGLEN,
	0,
	pkey_dh_init,
	pkey_dh_copy,
	pkey_dh_cleanup,
@@ -408,7 +541,7 @@ const EVP_PKEY_METHOD dh_pkey_meth =
const EVP_PKEY_METHOD dhx_pkey_meth = 
	{
	EVP_PKEY_DHX,
	EVP_PKEY_FLAG_AUTOARGLEN,
	0,
	pkey_dh_init,
	pkey_dh_copy,
	pkey_dh_cleanup,