summaryrefslogtreecommitdiffstats
path: root/crypto/cms
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2020-10-06 16:02:43 +0100
committerMatt Caswell <matt@openssl.org>2020-10-15 10:00:19 +0100
commit0b3a4ef27a6c2a427dc2d4a87c52677d57c90f4c (patch)
tree5615f080605adfb18b83b2ee6610a9a3daa15451 /crypto/cms
parent99b3b762c33fad9383cb2d1791be9c9f7d44710a (diff)
Move CMS enveloping code out of the algorithms and into CMS
There is quite a large amount of algorithm specific CMS code sitting in the algorithm directories. However, this seems to break layering. Algorithms really have no business knowing anything about CMS. Really it should be the other way around. Where there is algorithm specific CMS code it is the CMS layer that should know how to handle different algorithms. Therefore we move this code into the CMS layer. Reviewed-by: Shane Lontis <shane.lontis@oracle.com> (Merged from https://github.com/openssl/openssl/pull/13088)
Diffstat (limited to 'crypto/cms')
-rw-r--r--crypto/cms/build.info9
-rw-r--r--crypto/cms/cms_dh.c322
-rw-r--r--crypto/cms/cms_ecdh.c391
-rw-r--r--crypto/cms/cms_env.c9
-rw-r--r--crypto/cms/cms_err.c12
-rw-r--r--crypto/cms/cms_local.h4
-rw-r--r--crypto/cms/cms_rsa.c182
7 files changed, 928 insertions, 1 deletions
diff --git a/crypto/cms/build.info b/crypto/cms/build.info
index cb675436ef..2f5b653382 100644
--- a/crypto/cms/build.info
+++ b/crypto/cms/build.info
@@ -2,4 +2,11 @@ LIBS=../../libcrypto
SOURCE[../../libcrypto]= \
cms_lib.c cms_asn1.c cms_att.c cms_io.c cms_smime.c cms_err.c \
cms_sd.c cms_dd.c cms_cd.c cms_env.c cms_enc.c cms_ess.c \
- cms_pwri.c cms_kari.c
+ cms_pwri.c cms_kari.c cms_rsa.c
+
+IF[{- !$disabled{dh} -}]
+ SOURCE[../../libcrypto]=cms_dh.c
+ENDIF
+IF[{- !$disabled{ed} -}]
+ SOURCE[../../libcrypto]=cms_ecdh.c
+ENDIF \ No newline at end of file
diff --git a/crypto/cms/cms_dh.c b/crypto/cms/cms_dh.c
new file mode 100644
index 0000000000..aca609653c
--- /dev/null
+++ b/crypto/cms/cms_dh.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2006-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/cms.h>
+#include <openssl/err.h>
+#include <openssl/core_names.h>
+#include "cms_local.h"
+
+static int dh_cms_set_peerkey(EVP_PKEY_CTX *pctx,
+ X509_ALGOR *alg, ASN1_BIT_STRING *pubkey)
+{
+ const ASN1_OBJECT *aoid;
+ int atype;
+ const void *aval;
+ ASN1_INTEGER *public_key = NULL;
+ int rv = 0;
+ EVP_PKEY *pkpeer = NULL, *pk = 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 == NULL)
+ goto err;
+ if (!EVP_PKEY_is_a(pk, "DHX"))
+ goto err;
+
+ /* Get public key */
+ plen = ASN1_STRING_length(pubkey);
+ p = ASN1_STRING_get0_data(pubkey);
+ if (p == NULL || plen == 0)
+ goto err;
+
+ pkpeer = EVP_PKEY_new();
+ if (pkpeer == NULL
+ || !EVP_PKEY_copy_parameters(pkpeer, pk)
+ /*
+ * TODO(3.0): This is badly named!! Can we make this more
+ * generic and not TLS specific?
+ */
+ || !EVP_PKEY_set1_tls_encodedpoint(pkpeer, p, plen))
+ goto err;
+
+ if (EVP_PKEY_derive_set_peer(pctx, pkpeer) > 0)
+ rv = 1;
+ err:
+ ASN1_INTEGER_free(public_key);
+ EVP_PKEY_free(pkpeer);
+ 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 = 0;
+ 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) {
+ CMSerr(0, CMS_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 == NULL)
+ goto err;
+ kekctx = CMS_RecipientInfo_kari_get0_ctx(ri);
+ if (kekctx == NULL)
+ goto err;
+ kekcipher = EVP_get_cipherbyobj(kekalg->algorithm);
+ if (kekcipher == NULL || 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 != NULL) {
+ dukmlen = ASN1_STRING_length(ukm);
+ dukm = OPENSSL_memdup(ASN1_STRING_get0_data(ukm), dukmlen);
+ if (dukm == NULL)
+ goto err;
+ }
+
+ if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, dukmlen) <= 0)
+ goto err;
+ dukm = NULL;
+
+ rv = 1;
+ err:
+ X509_ALGOR_free(kekalg);
+ 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 == NULL)
+ 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 == NULL || pubkey == NULL)
+ 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;
+ const 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 = 0;
+ int rv = 0;
+ int kdf_type, wrap_nid;
+ const EVP_MD *kdf_md;
+
+ pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+ if (pctx == NULL)
+ 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)) {
+ BIGNUM *bn_pub_key = NULL;
+ ASN1_INTEGER *pubk;
+
+ if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, &bn_pub_key))
+ goto err;
+
+ pubk = BN_to_ASN1_INTEGER(bn_pub_key, NULL);
+ BN_free(bn_pub_key);
+ if (pubk == NULL)
+ 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 parameters 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 == NULL)
+ goto err;
+ wrap_alg->algorithm = OBJ_nid2obj(wrap_nid);
+ wrap_alg->parameter = ASN1_TYPE_new();
+ if (wrap_alg->parameter == NULL)
+ 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 != NULL) {
+ dukmlen = ASN1_STRING_length(ukm);
+ dukm = OPENSSL_memdup(ASN1_STRING_get0_data(ukm), dukmlen);
+ if (dukm == NULL)
+ 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 == NULL || penclen == 0)
+ goto err;
+ wrap_str = ASN1_STRING_new();
+ if (wrap_str == NULL)
+ 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:
+ OPENSSL_free(penc);
+ X509_ALGOR_free(wrap_alg);
+ OPENSSL_free(dukm);
+ return rv;
+}
+
+int cms_dh_envelope(CMS_RecipientInfo *ri, int decrypt)
+{
+ if (decrypt == 1)
+ return dh_cms_decrypt(ri);
+ else if (decrypt == 0)
+ return dh_cms_encrypt(ri);
+
+ CMSerr(0, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+ return 0;
+} \ No newline at end of file
diff --git a/crypto/cms/cms_ecdh.c b/crypto/cms/cms_ecdh.c
new file mode 100644
index 0000000000..b88be91211
--- /dev/null
+++ b/crypto/cms/cms_ecdh.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright 2006-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/cms.h>
+#include <openssl/err.h>
+#include <openssl/decoder.h>
+#include "cms_local.h"
+#include "crypto/evp.h"
+
+
+static EVP_PKEY *pkey_type2param(int ptype, const void *pval,
+ OPENSSL_CTX *libctx, const char *propq)
+{
+ EVP_PKEY *pkey = NULL;
+ EVP_PKEY_CTX *pctx = NULL;
+
+ if (ptype == V_ASN1_SEQUENCE) {
+ const ASN1_STRING *pstr = pval;
+ const unsigned char *pm = pstr->data;
+ int pmlen = pstr->length;
+ OSSL_DECODER_CTX *ctx = NULL;
+ BIO *membio = NULL;
+
+ /* TODO(3.0): Need to be able to specify here that only params will do */
+ ctx = OSSL_DECODER_CTX_new_by_EVP_PKEY(&pkey, "DER", "EC", libctx,
+ propq);
+ membio = BIO_new_mem_buf(pm, pmlen);
+ OSSL_DECODER_from_bio(ctx, membio);
+ BIO_free(membio);
+ OSSL_DECODER_CTX_free(ctx);
+ } else if (ptype == V_ASN1_OBJECT) {
+ const ASN1_OBJECT *poid = pval;
+ const char *groupname;
+
+ /*
+ * type == V_ASN1_OBJECT => the parameters are given by an asn1 OID
+ */
+
+ pctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", propq);
+
+ if (pctx == NULL)
+ goto err;
+ if (EVP_PKEY_paramgen_init(pctx) <= 0)
+ goto err;
+ groupname = OBJ_nid2sn(OBJ_obj2nid(poid));
+ if (groupname == NULL
+ || !EVP_PKEY_CTX_set_group_name(pctx, groupname)) {
+ CMSerr(0, CMS_R_DECODE_ERROR);
+ goto err;
+ }
+ if (EVP_PKEY_paramgen(pctx, &pkey) <= 0) {
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ }
+ } else {
+ CMSerr(0, CMS_R_DECODE_ERROR);
+ goto err;
+ }
+
+ return pkey;
+
+ err:
+ EVP_PKEY_free(pkey);
+ EVP_PKEY_CTX_free(pctx);
+ return NULL;
+}
+
+static int ecdh_cms_set_peerkey(EVP_PKEY_CTX *pctx,
+ X509_ALGOR *alg, ASN1_BIT_STRING *pubkey)
+{
+ const ASN1_OBJECT *aoid;
+ int atype;
+ const void *aval;
+ int rv = 0;
+ EVP_PKEY *pkpeer = NULL;
+ const unsigned char *p;
+ int plen;
+
+ X509_ALGOR_get0(&aoid, &atype, &aval, alg);
+ if (OBJ_obj2nid(aoid) != NID_X9_62_id_ecPublicKey)
+ goto err;
+ /* If absent parameters get group from main key */
+ if (atype == V_ASN1_UNDEF || atype == V_ASN1_NULL) {
+ EVP_PKEY *pk;
+ pk = EVP_PKEY_CTX_get0_pkey(pctx);
+ if (pk == NULL)
+ goto err;
+
+ pkpeer = EVP_PKEY_new();
+ if (pkpeer == NULL)
+ goto err;
+ if (!EVP_PKEY_copy_parameters(pkpeer, pk))
+ goto err;
+ } else {
+ /* TODO(3.0): Should the get0_libctx/propq calls actually be public API? */
+ pkpeer = pkey_type2param(atype, aval,
+ evp_pkey_ctx_get0_libctx(pctx),
+ evp_pkey_ctx_get0_propq(pctx));
+ if (pkpeer == NULL)
+ goto err;
+ }
+ /* We have parameters now set public key */
+ plen = ASN1_STRING_length(pubkey);
+ p = ASN1_STRING_get0_data(pubkey);
+ if (p == NULL || plen == 0)
+ goto err;
+
+ /* TODO(3.0): Terrible name. We need a non-tls specific name */
+ if (!EVP_PKEY_set1_tls_encodedpoint(pkpeer, p, plen))
+ goto err;
+
+ if (EVP_PKEY_derive_set_peer(pctx, pkpeer) > 0)
+ rv = 1;
+ err:
+ EVP_PKEY_free(pkpeer);
+ return rv;
+}
+
+/* Set KDF parameters based on KDF NID */
+static int ecdh_cms_set_kdf_param(EVP_PKEY_CTX *pctx, int eckdf_nid)
+{
+ int kdf_nid, kdfmd_nid, cofactor;
+ const EVP_MD *kdf_md;
+ if (eckdf_nid == NID_undef)
+ return 0;
+
+ /* Lookup KDF type, cofactor mode and digest */
+ if (!OBJ_find_sigid_algs(eckdf_nid, &kdfmd_nid, &kdf_nid))
+ return 0;
+
+ if (kdf_nid == NID_dh_std_kdf)
+ cofactor = 0;
+ else if (kdf_nid == NID_dh_cofactor_kdf)
+ cofactor = 1;
+ else
+ return 0;
+
+ if (EVP_PKEY_CTX_set_ecdh_cofactor_mode(pctx, cofactor) <= 0)
+ return 0;
+
+ if (EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, EVP_PKEY_ECDH_KDF_X9_63) <= 0)
+ return 0;
+
+ kdf_md = EVP_get_digestbynid(kdfmd_nid);
+ if (!kdf_md)
+ return 0;
+
+ if (EVP_PKEY_CTX_set_ecdh_kdf_md(pctx, kdf_md) <= 0)
+ return 0;
+ return 1;
+}
+
+static int ecdh_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 *der = NULL;
+ int plen, keylen;
+ EVP_CIPHER *kekcipher = NULL;
+ EVP_CIPHER_CTX *kekctx;
+ const char *name;
+
+ if (!CMS_RecipientInfo_kari_get0_alg(ri, &alg, &ukm))
+ return 0;
+
+ if (!ecdh_cms_set_kdf_param(pctx, OBJ_obj2nid(alg->algorithm))) {
+ CMSerr(0, CMS_R_KDF_PARAMETER_ERROR);
+ return 0;
+ }
+
+ if (alg->parameter->type != V_ASN1_SEQUENCE)
+ return 0;
+
+ p = alg->parameter->value.sequence->data;
+ plen = alg->parameter->value.sequence->length;
+ kekalg = d2i_X509_ALGOR(NULL, &p, plen);
+ if (kekalg == NULL)
+ goto err;
+ kekctx = CMS_RecipientInfo_kari_get0_ctx(ri);
+ if (kekctx == NULL)
+ goto err;
+ name = OBJ_nid2sn(OBJ_obj2nid(kekalg->algorithm));
+ kekcipher = EVP_CIPHER_fetch(pctx->libctx, name, pctx->propquery);
+ if (kekcipher == NULL || 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_ecdh_kdf_outlen(pctx, keylen) <= 0)
+ goto err;
+
+ plen = CMS_SharedInfo_encode(&der, kekalg, ukm, keylen);
+
+ if (plen <= 0)
+ goto err;
+
+ if (EVP_PKEY_CTX_set0_ecdh_kdf_ukm(pctx, der, plen) <= 0)
+ goto err;
+ der = NULL;
+
+ rv = 1;
+ err:
+ EVP_CIPHER_free(kekcipher);
+ X509_ALGOR_free(kekalg);
+ OPENSSL_free(der);
+ return rv;
+}
+
+static int ecdh_cms_decrypt(CMS_RecipientInfo *ri)
+{
+ EVP_PKEY_CTX *pctx;
+
+ pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+ if (pctx == NULL)
+ 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 (!ecdh_cms_set_peerkey(pctx, alg, pubkey)) {
+ CMSerr(0, CMS_R_PEER_KEY_ERROR);
+ return 0;
+ }
+ }
+ /* Set ECDH derivation parameters and initialise unwrap context */
+ if (!ecdh_cms_set_shared_info(pctx, ri)) {
+ CMSerr(0, CMS_R_SHARED_INFO_ERROR);
+ return 0;
+ }
+ return 1;
+}
+
+static int ecdh_cms_encrypt(CMS_RecipientInfo *ri)
+{
+ EVP_PKEY_CTX *pctx;
+ EVP_PKEY *pkey;
+ EVP_CIPHER_CTX *ctx;
+ int keylen;
+ X509_ALGOR *talg, *wrap_alg = NULL;
+ const ASN1_OBJECT *aoid;
+ ASN1_BIT_STRING *pubkey;
+ ASN1_STRING *wrap_str;
+ ASN1_OCTET_STRING *ukm;
+ unsigned char *penc = NULL;
+ size_t penclen;
+ int rv = 0;
+ int ecdh_nid, kdf_type, kdf_nid, wrap_nid;
+ const EVP_MD *kdf_md;
+
+ pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+ if (pctx == NULL)
+ 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)) {
+ /* Set the key */
+
+ /* TODO(3.0): Terrible name. Needs a non TLS specific name */
+ penclen = EVP_PKEY_get1_tls_encodedpoint(pkey, &penc);
+ 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_X9_62_id_ecPublicKey),
+ V_ASN1_UNDEF, NULL);
+ }
+
+ /* See if custom parameters set */
+ kdf_type = EVP_PKEY_CTX_get_ecdh_kdf_type(pctx);
+ if (kdf_type <= 0)
+ goto err;
+ if (!EVP_PKEY_CTX_get_ecdh_kdf_md(pctx, &kdf_md))
+ goto err;
+ ecdh_nid = EVP_PKEY_CTX_get_ecdh_cofactor_mode(pctx);
+ if (ecdh_nid < 0)
+ goto err;
+ else if (ecdh_nid == 0)
+ ecdh_nid = NID_dh_std_kdf;
+ else if (ecdh_nid == 1)
+ ecdh_nid = NID_dh_cofactor_kdf;
+
+ if (kdf_type == EVP_PKEY_ECDH_KDF_NONE) {
+ kdf_type = EVP_PKEY_ECDH_KDF_X9_63;
+ if (EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, kdf_type) <= 0)
+ goto err;
+ } else
+ /* Unknown KDF */
+ goto err;
+ if (kdf_md == NULL) {
+ /* Fixme later for better MD */
+ kdf_md = EVP_sha1();
+ if (EVP_PKEY_CTX_set_ecdh_kdf_md(pctx, kdf_md) <= 0)
+ goto err;
+ }
+
+ if (!CMS_RecipientInfo_kari_get0_alg(ri, &talg, &ukm))
+ goto err;
+
+ /* Lookup NID for KDF+cofactor+digest */
+
+ if (!OBJ_find_sigid_by_algs(&kdf_nid, EVP_MD_type(kdf_md), ecdh_nid))
+ goto err;
+ /* Get wrap NID */
+ ctx = CMS_RecipientInfo_kari_get0_ctx(ri);
+ wrap_nid = EVP_CIPHER_CTX_type(ctx);
+ keylen = EVP_CIPHER_CTX_key_length(ctx);
+
+ /* Package wrap algorithm in an AlgorithmIdentifier */
+
+ wrap_alg = X509_ALGOR_new();
+ if (wrap_alg == NULL)
+ goto err;
+ wrap_alg->algorithm = OBJ_nid2obj(wrap_nid);
+ wrap_alg->parameter = ASN1_TYPE_new();
+ if (wrap_alg->parameter == NULL)
+ 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_ecdh_kdf_outlen(pctx, keylen) <= 0)
+ goto err;
+
+ penclen = CMS_SharedInfo_encode(&penc, wrap_alg, ukm, keylen);
+
+ if (!penclen)
+ goto err;
+
+ if (EVP_PKEY_CTX_set0_ecdh_kdf_ukm(pctx, penc, penclen) <= 0)
+ goto err;
+ penc = NULL;
+
+ /*
+ * Now need to wrap encoding of wrap AlgorithmIdentifier into parameter
+ * of another AlgorithmIdentifier.
+ */
+ penclen = i2d_X509_ALGOR(wrap_alg, &penc);
+ if (!penc || !penclen)
+ goto err;
+ wrap_str = ASN1_STRING_new();
+ if (wrap_str == NULL)
+ goto err;
+ ASN1_STRING_set0(wrap_str, penc, penclen);
+ penc = NULL;
+ X509_ALGOR_set0(talg, OBJ_nid2obj(kdf_nid), V_ASN1_SEQUENCE, wrap_str);
+
+ rv = 1;
+
+ err:
+ OPENSSL_free(penc);
+ X509_ALGOR_free(wrap_alg);
+ return rv;
+}
+
+int cms_ecdh_envelope(CMS_RecipientInfo *ri, int decrypt)
+{
+ if (decrypt == 1)
+ return ecdh_cms_decrypt(ri);
+ else if (decrypt == 0)
+ return ecdh_cms_encrypt(ri);
+
+ CMSerr(0, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+ return 0;
+} \ No newline at end of file
diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c
index b1bba4c2d6..395e05ce55 100644
--- a/crypto/cms/cms_env.c
+++ b/crypto/cms/cms_env.c
@@ -115,6 +115,15 @@ int cms_env_asn1_ctrl(CMS_RecipientInfo *ri, int cmd)
return 0;
} else
return 0;
+
+ if (EVP_PKEY_is_a(pkey, "DHX"))
+ return cms_dh_envelope(ri, cmd);
+ else if (EVP_PKEY_is_a(pkey, "EC"))
+ return cms_ecdh_envelope(ri, cmd);
+ else if (EVP_PKEY_is_a(pkey, "RSA"))
+ return cms_rsa_envelope(ri, cmd);
+
+ /* Something else? We'll give engines etc a chance to handle this */
if (pkey->ameth == NULL || pkey->ameth->pkey_ctrl == NULL)
return 1;
i = pkey->ameth->pkey_ctrl(pkey, ASN1_PKEY_CTRL_CMS_ENVELOPE, cmd, ri);
diff --git a/crypto/cms/cms_err.c b/crypto/cms/cms_err.c
index da14c726c4..fdb2b7f5c8 100644
--- a/crypto/cms/cms_err.c
+++ b/crypto/cms/cms_err.c
@@ -47,6 +47,7 @@ static const ERR_STRING_DATA CMS_str_reasons[] = {
"content verify error"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_CTRL_ERROR), "ctrl error"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_CTRL_FAILURE), "ctrl failure"},
+ {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_DECODE_ERROR), "decode error"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_DECRYPT_ERROR), "decrypt error"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_ERROR_GETTING_PUBLIC_KEY),
"error getting public key"},
@@ -64,6 +65,11 @@ static const ERR_STRING_DATA CMS_str_reasons[] = {
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER),
"invalid key encryption parameter"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_INVALID_KEY_LENGTH), "invalid key length"},
+ {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_INVALID_LABEL), "invalid label"},
+ {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_INVALID_OAEP_PARAMETERS),
+ "invalid oaep parameters"},
+ {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_KDF_PARAMETER_ERROR),
+ "kdf parameter error"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_MD_BIO_INIT_ERROR), "md bio init error"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH),
"messagedigest attribute wrong length"},
@@ -102,11 +108,13 @@ static const ERR_STRING_DATA CMS_str_reasons[] = {
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NO_PUBLIC_KEY), "no public key"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NO_RECEIPT_REQUEST), "no receipt request"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NO_SIGNERS), "no signers"},
+ {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_PEER_KEY_ERROR), "peer key error"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE),
"private key does not match certificate"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_RECEIPT_DECODE_ERROR),
"receipt decode error"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_RECIPIENT_ERROR), "recipient error"},
+ {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_SHARED_INFO_ERROR), "shared info error"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_SIGNER_CERTIFICATE_NOT_FOUND),
"signer certificate not found"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_SIGNFINAL_ERROR), "signfinal error"},
@@ -131,10 +139,14 @@ static const ERR_STRING_DATA CMS_str_reasons[] = {
"unsupported compression algorithm"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNSUPPORTED_CONTENT_TYPE),
"unsupported content type"},
+ {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNSUPPORTED_ENCRYPTION_TYPE),
+ "unsupported encryption type"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNSUPPORTED_KEK_ALGORITHM),
"unsupported kek algorithm"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM),
"unsupported key encryption algorithm"},
+ {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNSUPPORTED_LABEL_SOURCE),
+ "unsupported label source"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNSUPPORTED_RECIPIENTINFO_TYPE),
"unsupported recipientinfo type"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNSUPPORTED_RECIPIENT_TYPE),
diff --git a/crypto/cms/cms_local.h b/crypto/cms/cms_local.h
index 336c354655..e162ea13ad 100644
--- a/crypto/cms/cms_local.h
+++ b/crypto/cms/cms_local.h
@@ -471,6 +471,10 @@ void cms_SignerInfos_set_cmsctx(CMS_ContentInfo *cms);
/* ESS routines */
int ess_check_signing_certs(CMS_SignerInfo *si, STACK_OF(X509) *chain);
+int cms_dh_envelope(CMS_RecipientInfo *ri, int decrypt);
+int cms_ecdh_envelope(CMS_RecipientInfo *ri, int decrypt);
+int cms_rsa_envelope(CMS_RecipientInfo *ri, int decrypt);
+
DECLARE_ASN1_ITEM(CMS_CertificateChoices)
DECLARE_ASN1_ITEM(CMS_DigestedData)
DECLARE_ASN1_ITEM(CMS_EncryptedData)
diff --git a/crypto/cms/cms_rsa.c b/crypto/cms/cms_rsa.c
new file mode 100644
index 0000000000..82d36d9b1f
--- /dev/null
+++ b/crypto/cms/cms_rsa.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2006-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/cms.h>
+#include <openssl/err.h>
+#include "crypto/asn1.h"
+#include "cms_local.h"
+
+
+static RSA_OAEP_PARAMS *rsa_oaep_decode(const X509_ALGOR *alg)
+{
+ RSA_OAEP_PARAMS *oaep;
+
+ oaep = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(RSA_OAEP_PARAMS),
+ alg->parameter);
+
+ if (oaep == NULL)
+ return NULL;
+
+ if (oaep->maskGenFunc != NULL) {
+ oaep->maskHash = x509_algor_mgf1_decode(oaep->maskGenFunc);
+ if (oaep->maskHash == NULL) {
+ RSA_OAEP_PARAMS_free(oaep);
+ return NULL;
+ }
+ }
+ return oaep;
+}
+
+static int rsa_cms_decrypt(CMS_RecipientInfo *ri)
+{
+ EVP_PKEY_CTX *pkctx;
+ X509_ALGOR *cmsalg;
+ int nid;
+ int rv = -1;
+ unsigned char *label = NULL;
+ int labellen = 0;
+ const EVP_MD *mgf1md = NULL, *md = NULL;
+ RSA_OAEP_PARAMS *oaep;
+
+ pkctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+ if (pkctx == NULL)
+ return 0;
+ if (!CMS_RecipientInfo_ktri_get0_algs(ri, NULL, NULL, &cmsalg))
+ return -1;
+ nid = OBJ_obj2nid(cmsalg->algorithm);
+ if (nid == NID_rsaEncryption)
+ return 1;
+ if (nid != NID_rsaesOaep) {
+ CMSerr(0, CMS_R_UNSUPPORTED_ENCRYPTION_TYPE);
+ return -1;
+ }
+ /* Decode OAEP parameters */
+ oaep = rsa_oaep_decode(cmsalg);
+
+ if (oaep == NULL) {
+ CMSerr(0, CMS_R_INVALID_OAEP_PARAMETERS);
+ goto err;
+ }
+
+ mgf1md = x509_algor_get_md(oaep->maskHash);
+ if (mgf1md == NULL)
+ goto err;
+ md = x509_algor_get_md(oaep->hashFunc);
+ if (md == NULL)
+ goto err;
+
+ if (oaep->pSourceFunc != NULL) {
+ X509_ALGOR *plab = oaep->pSourceFunc;
+
+ if (OBJ_obj2nid(plab->algorithm) != NID_pSpecified) {
+ CMSerr(0, CMS_R_UNSUPPORTED_LABEL_SOURCE);
+ goto err;
+ }
+ if (plab->parameter->type != V_ASN1_OCTET_STRING) {
+ CMSerr(0, CMS_R_INVALID_LABEL);
+ goto err;
+ }
+
+ label = plab->parameter->value.octet_string->data;
+ /* Stop label being freed when OAEP parameters are freed */
+ plab->parameter->value.octet_string->data = NULL;
+ labellen = plab->parameter->value.octet_string->length;
+ }
+
+ if (EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_OAEP_PADDING) <= 0)
+ goto err;
+ if (EVP_PKEY_CTX_set_rsa_oaep_md(pkctx, md) <= 0)
+ goto err;
+ if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkctx, mgf1md) <= 0)
+ goto err;
+ if (label != NULL
+ && EVP_PKEY_CTX_set0_rsa_oaep_label(pkctx, label, labellen) <= 0)
+ goto err;
+ /* Carry on */
+ rv = 1;
+
+ err:
+ RSA_OAEP_PARAMS_free(oaep);
+ return rv;
+}
+
+static int rsa_cms_encrypt(CMS_RecipientInfo *ri)
+{
+ const EVP_MD *md, *mgf1md;
+ RSA_OAEP_PARAMS *oaep = NULL;
+ ASN1_STRING *os = NULL;
+ X509_ALGOR *alg;
+ EVP_PKEY_CTX *pkctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+ int pad_mode = RSA_PKCS1_PADDING, rv = 0, labellen;
+ unsigned char *label;
+
+ if (CMS_RecipientInfo_ktri_get0_algs(ri, NULL, NULL, &alg) <= 0)
+ return 0;
+ if (pkctx) {
+ if (EVP_PKEY_CTX_get_rsa_padding(pkctx, &pad_mode) <= 0)
+ return 0;
+ }
+ if (pad_mode == RSA_PKCS1_PADDING) {
+ X509_ALGOR_set0(alg, OBJ_nid2obj(NID_rsaEncryption), V_ASN1_NULL, 0);
+ return 1;
+ }
+ /* Not supported */
+ if (pad_mode != RSA_PKCS1_OAEP_PADDING)
+ return 0;
+ if (EVP_PKEY_CTX_get_rsa_oaep_md(pkctx, &md) <= 0)
+ goto err;
+ if (EVP_PKEY_CTX_get_rsa_mgf1_md(pkctx, &mgf1md) <= 0)
+ goto err;
+ labellen = EVP_PKEY_CTX_get0_rsa_oaep_label(pkctx, &label);
+ if (labellen < 0)
+