From bd59f2b91db8fab86e8610de4565b5ab8de2b44b Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Sat, 20 Jul 2013 21:31:10 +0100 Subject: [PATCH] CMS RFC2631 X9.42 DH enveloped data support. --- crypto/dh/dh.h | 69 +++++++++ crypto/dh/dh_ameth.c | 350 ++++++++++++++++++++++++++++++++++++++++++- crypto/dh/dh_err.c | 8 +- crypto/dh/dh_pmeth.c | 147 +++++++++++++++++- 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 #include #include "asn1_locl.h" +#ifndef OPENSSL_NO_CMS +#include +#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 #endif +#include #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, -- 2.25.1