summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDr. Stephen Henson <steve@openssl.org>2013-07-20 21:31:10 +0100
committerDr. Stephen Henson <steve@openssl.org>2013-08-05 16:23:13 +0100
commitbd59f2b91db8fab86e8610de4565b5ab8de2b44b (patch)
tree5b4cb4b7514832fb234cd3559934055deb824822
parentdc1ce3bc64845d16314af1f11acf5384e4ae9b34 (diff)
downloadopenssl-new-bd59f2b91db8fab86e8610de4565b5ab8de2b44b.tar.gz
CMS RFC2631 X9.42 DH enveloped data support.
-rw-r--r--crypto/dh/dh.h69
-rw-r--r--crypto/dh/dh_ameth.c350
-rw-r--r--crypto/dh/dh_err.c8
-rw-r--r--crypto/dh/dh_pmeth.c147
4 files changed, 565 insertions, 9 deletions
diff --git a/crypto/dh/dh.h b/crypto/dh/dh.h
index a86c64db01..0cbb32e336 100644
--- a/crypto/dh/dh.h
+++ b/crypto/dh/dh.h
@@ -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
}
diff --git a/crypto/dh/dh_ameth.c b/crypto/dh/dh_ameth.c
index d19b8e9961..141c09ba54 100644
--- a/crypto/dh/dh_ameth.c
+++ b/crypto/dh/dh_ameth.c
@@ -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
diff --git a/crypto/dh/dh_err.c b/crypto/dh/dh_err.c
index a4c30b7ace..848c3cbf11 100644
--- a/crypto/dh/dh_err.c
+++ b/crypto/dh/dh_err.c
@@ -1,6 +1,6 @@
/* 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}
};
diff --git a/crypto/dh/dh_pmeth.c b/crypto/dh/dh_pmeth.c
index 095227bc2f..941801df46 100644
--- a/crypto/dh/dh_pmeth.c
+++ b/crypto/dh/dh_pmeth.c
@@ -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,7 +154,13 @@ 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);
- if (ret < 0)
+ 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;
- *keylen = 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,