Also add support for ML-KEM in CMS (draft-ietf-lamps-cms-kyber).
Add the -recip_kdf and -recip_ukm parameters to `openssl cms -encrypt`
to allow the user to specify the KDF algorithm and optional user
keying material for each recipient.
A provider may indicate which RecipientInfo type is supported
for a key, otherwise CMS will try to figure it out itself. A
provider may also indicate which KDF to use in KEMRecipientInfo
if the user hasn't specified one.
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/27681)
providers/common/der/der_wrap_gen.c
providers/common/der/der_sm2_gen.c
providers/common/der/der_ml_dsa_gen.c
+providers/common/der/der_hkdf_gen.c
providers/common/include/prov/der_slh_dsa.h
providers/common/include/prov/der_dsa.h
providers/common/include/prov/der_ec.h
providers/common/include/prov/der_wrap.h
providers/common/include/prov/der_sm2.h
providers/common/include/prov/der_ml_dsa.h
+providers/common/include/prov/der_hkdf.h
providers/implementations/keymgmt/ml_dsa_kmgmt.c
providers/implementations/keymgmt/ml_kem_kmgmt.c
providers/implementations/keymgmt/mlx_kmgmt.c
*Michael Krueger, Martin Rauch*
+ * Added KEMRecipientInfo (RFC 9629) and ML-KEM (draft-ietf-lamps-cms-kyber)
+ support to CMS.
+
+ *Daniel Van Geest (CryptoNext Security)*
+
OpenSSL 3.5
-----------
static int verify_err = 0;
typedef struct cms_key_param_st cms_key_param;
+typedef struct cms_recip_opt_st cms_recip_opt;
struct cms_key_param_st {
int idx;
cms_key_param *next;
};
+struct cms_recip_opt_st {
+ int idx;
+ const char *kdf;
+ unsigned char *ukm_data;
+ long ukm_data_length;
+ cms_recip_opt *next;
+};
+
typedef enum OPTION_choice {
OPT_COMMON,
OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_ENCRYPT,
OPT_PROV_ENUM, OPT_CONFIG,
OPT_V_ENUM,
OPT_CIPHER, OPT_KEKCIPHER,
- OPT_ORIGINATOR
+ OPT_ORIGINATOR,
+ OPT_RECIP_UKM, OPT_RECIP_KDF
} OPTION_CHOICE;
const OPTIONS cms_options[] = {
{"kekcipher", OPT_KEKCIPHER, 's',
"The key encryption algorithm to use"},
{"wrap", OPT_WRAP, 's',
- "Key wrap algorithm to use when encrypting with key agreement"},
+ "Key wrap algorithm to use when encrypting with key agreement or key encapsulation"},
{"aes128-wrap", OPT_AES128_WRAP, '-', "Use AES128 to wrap key"},
{"aes192-wrap", OPT_AES192_WRAP, '-', "Use AES192 to wrap key"},
{"aes256-wrap", OPT_AES256_WRAP, '-', "Use AES256 to wrap key"},
{"des3-wrap", OPT_3DES_WRAP, '-', "Use 3DES-EDE to wrap key"},
{"debug_decrypt", OPT_DEBUG_DECRYPT, '-',
"Disable MMA protection, return error if no recipient found (see doc)"},
+ {"recip_kdf", OPT_RECIP_KDF, 's', "Set KEMRecipientInfo KDF for current recipient"},
+ {"recip_ukm", OPT_RECIP_UKM, 's', "KEMRecipientInfo user keying material for current recipient, in hex notation"},
OPT_SECTION("Signing"),
{"md", OPT_MD, 's', "Digest algorithm to use"},
return NULL;
}
+static cms_recip_opt *alloc_recip_opt(int recipidx)
+{
+ cms_recip_opt *opt;
+
+ opt = app_malloc(sizeof(*opt), "recipient options buffer");
+ opt->idx = recipidx;
+ opt->next = NULL;
+ opt->kdf = NULL;
+ opt->ukm_data = NULL;
+ opt->ukm_data_length = 0;
+ return opt;
+}
+
int cms_main(int argc, char **argv)
{
CONF *conf = NULL;
size_t secret_keylen = 0, secret_keyidlen = 0;
unsigned char *pwri_pass = NULL, *pwri_tmp = NULL;
unsigned char *secret_key = NULL, *secret_keyid = NULL;
+ cms_recip_opt *recip_first = NULL, *recip_opt = NULL;
+ int recipidx = -1;
long ltmp;
const char *mime_eol = "\n";
OPTION_CHOICE o;
recipfile = opt_arg();
}
break;
+ case OPT_RECIP_KDF:
+ case OPT_RECIP_UKM:
+ recipidx = -1;
+ if (operation == SMIME_ENCRYPT) {
+ if (sk_X509_num(encerts) > 0)
+ recipidx += sk_X509_num(encerts);
+ }
+ if (recipidx < 0) {
+ BIO_printf(bio_err, "No recipient specified\n");
+ goto opthelp;
+ }
+ if (recip_opt == NULL || recip_opt->idx != recipidx) {
+ cms_recip_opt *nopt;
+
+ nopt = alloc_recip_opt(recipidx);
+ if (recip_first == NULL)
+ recip_first = nopt;
+ else
+ recip_opt->next = nopt;
+ recip_opt = nopt;
+ }
+ if (o == OPT_RECIP_KDF) {
+ if (recip_opt->kdf != NULL) {
+ BIO_puts(bio_err, "Illegal multiple -recip_kdf for one -recip\n");
+ goto end;
+ }
+ recip_opt->kdf = opt_arg();
+ } else {
+ if (recip_opt->ukm_data != NULL) {
+ BIO_puts(bio_err, "Illegal multiple -recip_ukm for one -recip\n");
+ goto end;
+ }
+ recip_opt->ukm_data = OPENSSL_hexstr2buf(opt_arg(),
+ &recip_opt->ukm_data_length);
+ if (recip_opt->ukm_data == NULL) {
+ BIO_printf(bio_err, "Invalid hex value after -recip_ukm\n");
+ goto end;
+ }
+ }
+ break;
case OPT_CIPHER:
ciphername = opt_unknown();
break;
if (operation != SMIME_ENCRYPT && *argv != NULL)
BIO_printf(bio_err,
"Warning: recipient certificate file parameters ignored for operation other than -encrypt\n");
+ if (operation != SMIME_ENCRYPT && recip_first != NULL)
+ BIO_printf(bio_err,
+ "Warning: -recip_kdf and -recip_ukm parameters ignored for operation other than -encrypt\n");
if ((flags & CMS_BINARY) != 0) {
if (!(operation & SMIME_OP))
goto end;
for (i = 0; i < sk_X509_num(encerts); i++) {
CMS_RecipientInfo *ri;
+ int ri_type;
cms_key_param *kparam;
+ cms_recip_opt *ropt;
int tflags = flags | CMS_KEY_PARAM;
/* This flag enforces allocating the EVP_PKEY_CTX for the recipient here */
EVP_PKEY_CTX *pctx;
int res;
for (kparam = key_first; kparam; kparam = kparam->next) {
- if (kparam->idx == i) {
+ if (kparam->idx == i)
+ break;
+ }
+ for (ropt = recip_first; ropt; ropt = ropt->next) {
+ if (ropt->idx == i)
break;
- }
}
ri = CMS_add1_recipient(cms, x, key, originator, tflags);
if (ri == NULL)
goto end;
+ ri_type = CMS_RecipientInfo_type(ri);
+
pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
if (pctx != NULL && kparam != NULL) {
if (!cms_set_pkey_param(pctx, kparam->param))
if (res <= 0 && res != -2)
goto end;
- if (CMS_RecipientInfo_type(ri) == CMS_RECIPINFO_AGREE
- && wrap_cipher != NULL) {
- EVP_CIPHER_CTX *wctx;
- wctx = CMS_RecipientInfo_kari_get0_ctx(ri);
- if (EVP_EncryptInit_ex(wctx, wrap_cipher, NULL, NULL, NULL) != 1)
- goto end;
+ if (wrap_cipher != NULL) {
+ EVP_CIPHER_CTX *wctx = NULL;
+
+ if (ri_type == CMS_RECIPINFO_AGREE)
+ wctx = CMS_RecipientInfo_kari_get0_ctx(ri);
+ else if (ri_type == CMS_RECIPINFO_KEM)
+ wctx = CMS_RecipientInfo_kemri_get0_ctx(ri);
+ if (wctx != NULL) {
+ if (EVP_EncryptInit_ex(wctx, wrap_cipher, NULL, NULL, NULL) != 1)
+ goto end;
+ }
+ }
+
+ if (ropt != NULL && ri_type == CMS_RECIPINFO_KEM) {
+ if (ropt->ukm_data != NULL) {
+ if (!CMS_RecipientInfo_kemri_set_ukm(ri, ropt->ukm_data,
+ (int)ropt->ukm_data_length))
+ goto end;
+ }
+ if (ropt->kdf != NULL) {
+ X509_ALGOR *kdf_algo;
+ ASN1_OBJECT *kdf_obj;
+
+ kdf_algo = CMS_RecipientInfo_kemri_get0_kdf_alg(ri);
+ kdf_obj = OBJ_txt2obj(ropt->kdf, 0);
+ if (kdf_obj == NULL) {
+ BIO_printf(bio_err, "Unknown KDF %s\n", ropt->kdf);
+ goto end;
+ }
+ /* Only works for OIDs without params */
+ if (!X509_ALGOR_set0(kdf_algo, kdf_obj, V_ASN1_UNDEF, NULL))
+ goto end;
+ }
}
}
OPENSSL_free(key_param);
key_param = tparam;
}
+ for (recip_opt = recip_first; recip_opt != NULL;) {
+ cms_recip_opt *topt;
+
+ OPENSSL_free(recip_opt->ukm_data);
+ topt = recip_opt->next;
+ OPENSSL_free(recip_opt);
+ recip_opt = topt;
+ }
X509_STORE_free(store);
X509_free(cert);
X509_free(recip);
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_rsa.c cms_dh.c cms_ec.c
+ cms_pwri.c cms_kari.c cms_rsa.c cms_dh.c cms_ec.c cms_kem.c cms_kemri.c
ASN1_SIMPLE(CMS_PasswordRecipientInfo, encryptedKey, ASN1_OCTET_STRING)
} ASN1_SEQUENCE_END(CMS_PasswordRecipientInfo)
+static int cms_kemri_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+ void *exarg)
+{
+ CMS_KEMRecipientInfo *kemri = (CMS_KEMRecipientInfo *)*pval;
+
+ if (operation == ASN1_OP_NEW_POST) {
+ kemri->ctx = EVP_CIPHER_CTX_new();
+ if (kemri->ctx == NULL)
+ return 0;
+ EVP_CIPHER_CTX_set_flags(kemri->ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+ kemri->pctx = NULL;
+ } else if (operation == ASN1_OP_FREE_POST) {
+ EVP_PKEY_CTX_free(kemri->pctx);
+ EVP_CIPHER_CTX_free(kemri->ctx);
+ ASN1_OCTET_STRING_free(kemri->ukm);
+ }
+ return 1;
+}
+
+ASN1_SEQUENCE_cb(CMS_KEMRecipientInfo, cms_kemri_cb) = {
+ ASN1_EMBED(CMS_KEMRecipientInfo, version, INT32),
+ ASN1_SIMPLE(CMS_KEMRecipientInfo, rid, CMS_SignerIdentifier),
+ ASN1_SIMPLE(CMS_KEMRecipientInfo, kem, X509_ALGOR),
+ ASN1_SIMPLE(CMS_KEMRecipientInfo, kemct, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(CMS_KEMRecipientInfo, kdf, X509_ALGOR),
+ ASN1_EMBED(CMS_KEMRecipientInfo, kekLength, INT32),
+ ASN1_EXP_OPT(CMS_KEMRecipientInfo, ukm, ASN1_OCTET_STRING, 0),
+ ASN1_SIMPLE(CMS_KEMRecipientInfo, wrap, X509_ALGOR),
+ ASN1_SIMPLE(CMS_KEMRecipientInfo, encryptedKey, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END_cb(CMS_KEMRecipientInfo, CMS_KEMRecipientInfo)
+
+ASN1_ADB_TEMPLATE(ori_def) = ASN1_SIMPLE(CMS_OtherRecipientInfo, d.other, ASN1_ANY);
+
+ASN1_ADB(CMS_OtherRecipientInfo) = {
+ ADB_ENTRY(NID_id_smime_ori_kem, ASN1_SIMPLE(CMS_OtherRecipientInfo, d.kemri,
+ CMS_KEMRecipientInfo))
+} ASN1_ADB_END(CMS_OtherRecipientInfo, 0, oriType, 0, &ori_def_tt, NULL);
+
+DECLARE_ASN1_FUNCTIONS(CMS_OtherRecipientInfo)
+
ASN1_SEQUENCE(CMS_OtherRecipientInfo) = {
ASN1_SIMPLE(CMS_OtherRecipientInfo, oriType, ASN1_OBJECT),
- ASN1_OPT(CMS_OtherRecipientInfo, oriValue, ASN1_ANY)
-} static_ASN1_SEQUENCE_END(CMS_OtherRecipientInfo)
+ ASN1_ADB_OBJECT(CMS_OtherRecipientInfo)
+} ASN1_SEQUENCE_END(CMS_OtherRecipientInfo)
+
+IMPLEMENT_ASN1_FUNCTIONS(CMS_OtherRecipientInfo)
/* Free up RecipientInfo additional data */
static int cms_ri_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
CMS_PasswordRecipientInfo *pwri = ri->d.pwri;
OPENSSL_clear_free(pwri->pass, pwri->passlen);
}
+ } else if (operation == ASN1_OP_D2I_POST) {
+ CMS_RecipientInfo *ri = (CMS_RecipientInfo *)*pval;
+
+ ri->type = ri->encoded_type;
+ if (ri->type == CMS_RECIPINFO_OTHER) {
+ int nid;
+
+ nid = OBJ_obj2nid(ri->d.ori->oriType);
+ /* For ORI, map NID to specific type */
+ if (nid == NID_id_smime_ori_kem)
+ ri->type = CMS_RECIPINFO_KEM;
+ /* Otherwise stay with generic CMS_RECIPINFO_OTHER type */
+ }
+ } else if (operation == ASN1_OP_NEW_POST) {
+ CMS_RecipientInfo *ri = (CMS_RecipientInfo *)*pval;
+
+ ri->type = ri->encoded_type;
}
return 1;
}
ASN1_IMP(CMS_RecipientInfo, d.kekri, CMS_KEKRecipientInfo, 2),
ASN1_IMP(CMS_RecipientInfo, d.pwri, CMS_PasswordRecipientInfo, 3),
ASN1_IMP(CMS_RecipientInfo, d.ori, CMS_OtherRecipientInfo, 4)
-} ASN1_CHOICE_END_cb(CMS_RecipientInfo, CMS_RecipientInfo, type)
+} ASN1_CHOICE_END_cb(CMS_RecipientInfo, CMS_RecipientInfo, encoded_type)
ASN1_NDEF_SEQUENCE(CMS_EnvelopedData) = {
ASN1_EMBED(CMS_EnvelopedData, version, INT32),
intsi.pecsi = &ecsi;
return ASN1_item_i2d(intsi.a, pder, ASN1_ITEM_rptr(CMS_SharedInfo));
}
+
+/*
+ * Utilities to encode the CMS_CMSORIforKEMOtherInfo structure used during key
+ * derivation.
+ */
+
+typedef struct {
+ X509_ALGOR *wrap;
+ uint32_t kekLength;
+ ASN1_OCTET_STRING *ukm;
+} CMS_CMSORIforKEMOtherInfo;
+
+ASN1_SEQUENCE(CMS_CMSORIforKEMOtherInfo) = {
+ ASN1_SIMPLE(CMS_CMSORIforKEMOtherInfo, wrap, X509_ALGOR),
+ ASN1_EMBED(CMS_CMSORIforKEMOtherInfo, kekLength, INT32),
+ ASN1_EXP_OPT(CMS_CMSORIforKEMOtherInfo, ukm, ASN1_OCTET_STRING, 0),
+} static_ASN1_SEQUENCE_END(CMS_CMSORIforKEMOtherInfo)
+
+int CMS_CMSORIforKEMOtherInfo_encode(unsigned char **pder, X509_ALGOR *wrap,
+ ASN1_OCTET_STRING *ukm, int keylen)
+{
+ CMS_CMSORIforKEMOtherInfo kem_otherinfo;
+
+ kem_otherinfo.wrap = wrap;
+ kem_otherinfo.kekLength = keylen;
+ kem_otherinfo.ukm = ukm;
+
+ return ASN1_item_i2d((ASN1_VALUE *)&kem_otherinfo, pder,
+ ASN1_ITEM_rptr(CMS_CMSORIforKEMOtherInfo));
+}
* https://www.openssl.org/source/license.html
*/
+/*
+ * Low level key APIs (DH etc) are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
#include "internal/cryptlib.h"
#include <openssl/asn1t.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/cms.h>
#include <openssl/evp.h>
+#include <openssl/core_names.h>
#include "internal/sizes.h"
#include "crypto/asn1.h"
#include "crypto/evp.h"
{
EVP_PKEY *pkey;
int i;
- if (ri->type == CMS_RECIPINFO_TRANS)
+
+ switch (ri->type) {
+ case CMS_RECIPINFO_TRANS:
pkey = ri->d.ktri->pkey;
- else if (ri->type == CMS_RECIPINFO_AGREE) {
+ break;
+ case CMS_RECIPINFO_AGREE: {
EVP_PKEY_CTX *pctx = ri->d.kari->pctx;
if (pctx == NULL)
pkey = EVP_PKEY_CTX_get0_pkey(pctx);
if (pkey == NULL)
return 0;
- } else
+ break;
+ }
+ case CMS_RECIPINFO_KEM:
+ return ossl_cms_kem_envelope(ri, cmd);
+ default:
return 0;
+ }
if (EVP_PKEY_is_a(pkey, "DHX") || EVP_PKEY_is_a(pkey, "DH"))
return ossl_cms_dh_envelope(ri, cmd);
case CMS_RECIPINFO_PASS:
ri->d.pwri->cms_ctx = ctx;
break;
+ case CMS_RECIPINFO_KEM:
+ ri->d.ori->d.kemri->cms_ctx = ctx;
+ break;
default:
break;
}
return ri->d.ktri->pctx;
else if (ri->type == CMS_RECIPINFO_AGREE)
return ri->d.kari->pctx;
+ else if (ri->type == CMS_RECIPINFO_KEM)
+ return ri->d.ori->d.kemri->pctx;
return NULL;
}
ri->d.ktri = M_ASN1_new_of(CMS_KeyTransRecipientInfo);
if (!ri->d.ktri)
return 0;
- ri->type = CMS_RECIPINFO_TRANS;
+ ri->encoded_type = ri->type = CMS_RECIPINFO_TRANS;
ktri = ri->d.ktri;
ktri->cms_ctx = ctx;
goto err;
break;
+ case CMS_RECIPINFO_KEM:
+ if (!ossl_cms_RecipientInfo_kemri_init(ri, recip, pk, flags, ctx))
+ goto err;
+ break;
+
default:
ERR_raise(ERR_LIB_CMS, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
goto err;
ERR_raise(ERR_LIB_CMS, ERR_R_ASN1_LIB);
goto err;
}
- ri->type = CMS_RECIPINFO_KEK;
+ ri->encoded_type = ri->type = CMS_RECIPINFO_KEK;
kekri = ri->d.kekri;
case CMS_RECIPINFO_PASS:
return ossl_cms_RecipientInfo_pwri_crypt(cms, ri, 0);
+ case CMS_RECIPINFO_KEM:
+ return ossl_cms_RecipientInfo_kemri_decrypt(cms, ri);
+
default:
ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_RECIPIENTINFO_TYPE);
return 0;
case CMS_RECIPINFO_PASS:
return ossl_cms_RecipientInfo_pwri_crypt(cms, ri, 1);
+ case CMS_RECIPINFO_KEM:
+ return ossl_cms_RecipientInfo_kemri_encrypt(cms, ri);
+
default:
ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_RECIPIENT_TYPE);
return 0;
for (i = 0; i < sk_CMS_RecipientInfo_num(env->recipientInfos); i++) {
ri = sk_CMS_RecipientInfo_value(env->recipientInfos, i);
- if (ri->type == CMS_RECIPINFO_PASS || ri->type == CMS_RECIPINFO_OTHER) {
+ if (ri->type == CMS_RECIPINFO_PASS || ri->type == CMS_RECIPINFO_OTHER
+ || ri->type == CMS_RECIPINFO_KEM) {
env->version = 3;
return;
} else if (ri->type != CMS_RECIPINFO_TRANS
*/
int ossl_cms_pkey_get_ri_type(EVP_PKEY *pk)
{
+ int ri_type;
+ EVP_PKEY_CTX *ctx = NULL;
+
+ /*
+ * First check the provider for RecipientInfo support since a key may support
+ * multiple types, e.g. an RSA key and provider may support RSA key transport
+ * and/or RSA-KEM.
+ */
+ if (evp_pkey_is_provided(pk)
+ && EVP_PKEY_get_int_param(pk, OSSL_PKEY_PARAM_CMS_RI_TYPE, &ri_type))
+ return ri_type;
+
/* Check types that we know about */
if (EVP_PKEY_is_a(pk, "DH"))
return CMS_RECIPINFO_AGREE;
return CMS_RECIPINFO_TRANS;
/*
- * Otherwise this might ben an engine implementation, so see if we can get
+ * Otherwise this might be an engine implementation, so see if we can get
* the type from the ameth.
*/
if (pk->ameth && pk->ameth->pkey_ctrl) {
if (i > 0)
return r;
}
- return CMS_RECIPINFO_TRANS;
+
+ /*
+ * Otherwise try very hard to figure out what RecipientInfo the key supports.
+ */
+ ri_type = CMS_RECIPINFO_TRANS;
+ ctx = EVP_PKEY_CTX_new(pk, NULL);
+ if (ctx != NULL) {
+ ERR_set_mark();
+ if (EVP_PKEY_encrypt_init(ctx) > 0)
+ ri_type = CMS_RECIPINFO_TRANS;
+ else if (EVP_PKEY_derive_init(ctx) > 0)
+ ri_type = CMS_RECIPINFO_AGREE;
+ else if (EVP_PKEY_encapsulate_init(ctx, NULL) > 0)
+ ri_type = CMS_RECIPINFO_KEM;
+ ERR_pop_to_mark();
+ }
+ EVP_PKEY_CTX_free(ctx);
+
+ return ri_type;
}
int ossl_cms_pkey_is_ri_type_supported(EVP_PKEY *pk, int ri_type)
return (supportedRiType == ri_type);
}
+
+int ossl_cms_RecipientInfo_wrap_init(CMS_RecipientInfo *ri,
+ const EVP_CIPHER *cipher)
+{
+ const CMS_CTX *cms_ctx;
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *kekcipher;
+ EVP_CIPHER *fetched_kekcipher;
+ const char *kekcipher_name;
+ int keylen;
+ int ret;
+
+ if (ri->type == CMS_RECIPINFO_AGREE) {
+ cms_ctx = ri->d.kari->cms_ctx;
+ ctx = ri->d.kari->ctx;
+ } else if (ri->type == CMS_RECIPINFO_KEM) {
+ cms_ctx = ri->d.ori->d.kemri->cms_ctx;
+ ctx = ri->d.ori->d.kemri->ctx;
+ } else {
+ ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_RECIPIENTINFO_TYPE);
+ return 0;
+ }
+
+ /* If a suitable wrap algorithm is already set nothing to do */
+ kekcipher = EVP_CIPHER_CTX_get0_cipher(ctx);
+ if (kekcipher != NULL) {
+ if (EVP_CIPHER_CTX_get_mode(ctx) != EVP_CIPH_WRAP_MODE)
+ return 0;
+ return 1;
+ }
+ if (cipher == NULL)
+ return 0;
+ keylen = EVP_CIPHER_get_key_length(cipher);
+ if (keylen <= 0) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if ((EVP_CIPHER_get_flags(cipher) & EVP_CIPH_FLAG_GET_WRAP_CIPHER) != 0) {
+ ret = EVP_CIPHER_meth_get_ctrl(cipher)(NULL, EVP_CTRL_GET_WRAP_CIPHER,
+ 0, &kekcipher);
+ if (ret <= 0)
+ return 0;
+
+ if (kekcipher != NULL) {
+ if (EVP_CIPHER_get_mode(kekcipher) != EVP_CIPH_WRAP_MODE)
+ return 0;
+ kekcipher_name = EVP_CIPHER_get0_name(kekcipher);
+ goto enc;
+ }
+ }
+
+ /*
+ * Pick a cipher based on content encryption cipher. If it is DES3 use
+ * DES3 wrap otherwise use AES wrap similar to key size.
+ */
+#ifndef OPENSSL_NO_DES
+ if (EVP_CIPHER_get_type(cipher) == NID_des_ede3_cbc)
+ kekcipher_name = SN_id_smime_alg_CMS3DESwrap;
+ else
+#endif
+ if (keylen <= 16)
+ kekcipher_name = SN_id_aes128_wrap;
+ else if (keylen <= 24)
+ kekcipher_name = SN_id_aes192_wrap;
+ else
+ kekcipher_name = SN_id_aes256_wrap;
+enc:
+ fetched_kekcipher = EVP_CIPHER_fetch(ossl_cms_ctx_get0_libctx(cms_ctx),
+ kekcipher_name,
+ ossl_cms_ctx_get0_propq(cms_ctx));
+ if (fetched_kekcipher == NULL)
+ return 0;
+ ret = EVP_EncryptInit_ex(ctx, fetched_kekcipher, NULL, NULL, NULL);
+ EVP_CIPHER_free(fetched_kekcipher);
+ return ret;
+}
"not a signed receipt"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NOT_ENCRYPTED_DATA), "not encrypted data"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NOT_KEK), "not kek"},
+ {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NOT_KEM), "not kem"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NOT_KEY_AGREEMENT), "not key agreement"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NOT_KEY_TRANSPORT), "not key transport"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_NOT_PWRI), "not pwri"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNKNOWN_DIGEST_ALGORITHM),
"unknown digest algorithm"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNKNOWN_ID), "unknown id"},
+ {ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNKNOWN_KDF_ALGORITHM),
+ "unknown kdf algorithm"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM),
"unsupported compression algorithm"},
{ERR_PACK(ERR_LIB_CMS, 0, CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM),
"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_KDF_ALGORITHM),
+ "unsupported kdf algorithm"},
{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),
* https://www.openssl.org/source/license.html
*/
-/*
- * Low level key APIs (DH etc) are deprecated for public use, but still ok for
- * internal use.
- */
-#include "internal/deprecated.h"
-
#include "internal/cryptlib.h"
#include <openssl/asn1t.h>
#include <openssl/pem.h>
ri->d.kari = M_ASN1_new_of(CMS_KeyAgreeRecipientInfo);
if (ri->d.kari == NULL)
return 0;
- ri->type = CMS_RECIPINFO_AGREE;
+ ri->encoded_type = ri->type = CMS_RECIPINFO_AGREE;
kari = ri->d.kari;
kari->version = 3;
return 1;
}
-static int cms_wrap_init(CMS_KeyAgreeRecipientInfo *kari,
- const EVP_CIPHER *cipher)
-{
- const CMS_CTX *cms_ctx = kari->cms_ctx;
- EVP_CIPHER_CTX *ctx = kari->ctx;
- const EVP_CIPHER *kekcipher;
- EVP_CIPHER *fetched_kekcipher;
- const char *kekcipher_name;
- int keylen;
- int ret;
-
- /* If a suitable wrap algorithm is already set nothing to do */
- kekcipher = EVP_CIPHER_CTX_get0_cipher(ctx);
- if (kekcipher != NULL) {
- if (EVP_CIPHER_CTX_get_mode(ctx) != EVP_CIPH_WRAP_MODE)
- return 0;
- return 1;
- }
- if (cipher == NULL)
- return 0;
- keylen = EVP_CIPHER_get_key_length(cipher);
- if ((EVP_CIPHER_get_flags(cipher) & EVP_CIPH_FLAG_GET_WRAP_CIPHER) != 0) {
- ret = EVP_CIPHER_meth_get_ctrl(cipher)(NULL, EVP_CTRL_GET_WRAP_CIPHER,
- 0, &kekcipher);
- if (ret <= 0)
- return 0;
-
- if (kekcipher != NULL) {
- if (EVP_CIPHER_get_mode(kekcipher) != EVP_CIPH_WRAP_MODE)
- return 0;
- kekcipher_name = EVP_CIPHER_get0_name(kekcipher);
- goto enc;
- }
- }
-
- /*
- * Pick a cipher based on content encryption cipher. If it is DES3 use
- * DES3 wrap otherwise use AES wrap similar to key size.
- */
-#ifndef OPENSSL_NO_DES
- if (EVP_CIPHER_get_type(cipher) == NID_des_ede3_cbc)
- kekcipher_name = SN_id_smime_alg_CMS3DESwrap;
- else
-#endif
- if (keylen <= 16)
- kekcipher_name = SN_id_aes128_wrap;
- else if (keylen <= 24)
- kekcipher_name = SN_id_aes192_wrap;
- else
- kekcipher_name = SN_id_aes256_wrap;
-enc:
- fetched_kekcipher = EVP_CIPHER_fetch(ossl_cms_ctx_get0_libctx(cms_ctx),
- kekcipher_name,
- ossl_cms_ctx_get0_propq(cms_ctx));
- if (fetched_kekcipher == NULL)
- return 0;
- ret = EVP_EncryptInit_ex(ctx, fetched_kekcipher, NULL, NULL, NULL);
- EVP_CIPHER_free(fetched_kekcipher);
- return ret;
-}
-
/* Encrypt content key in key agreement recipient info */
int ossl_cms_RecipientInfo_kari_encrypt(const CMS_ContentInfo *cms,
reks = kari->recipientEncryptedKeys;
ec = ossl_cms_get0_env_enc_content(cms);
/* Initialise wrap algorithm parameters */
- if (!cms_wrap_init(kari, ec->cipher))
+ if (!ossl_cms_RecipientInfo_wrap_init(ri, ec->cipher))
return 0;
/*
* If no originator key set up initialise for ephemeral key the public key
--- /dev/null
+/*
+ * Copyright 2025 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 <assert.h>
+#include <limits.h>
+#include <openssl/cms.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/decoder.h>
+#include "internal/sizes.h"
+#include "crypto/asn1.h"
+#include "crypto/evp.h"
+#include "cms_local.h"
+
+static int kem_cms_decrypt(CMS_RecipientInfo *ri)
+{
+ uint32_t *kekLength;
+ X509_ALGOR *wrap;
+ EVP_PKEY_CTX *pctx;
+ EVP_CIPHER_CTX *kekctx;
+ uint32_t cipher_length;
+ char name[OSSL_MAX_NAME_SIZE];
+ EVP_CIPHER *kekcipher = NULL;
+ int rv = 0;
+
+ if (!ossl_cms_RecipientInfo_kemri_get0_alg(ri, &kekLength, &wrap))
+ goto err;
+
+ pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+ if (pctx == NULL)
+ goto err;
+
+ kekctx = CMS_RecipientInfo_kemri_get0_ctx(ri);
+ if (kekctx == NULL)
+ goto err;
+
+ OBJ_obj2txt(name, sizeof(name), wrap->algorithm, 0);
+ kekcipher = EVP_CIPHER_fetch(pctx->libctx, name, pctx->propquery);
+ if (kekcipher == NULL || EVP_CIPHER_get_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, wrap->parameter) <= 0)
+ goto err;
+
+ cipher_length = EVP_CIPHER_CTX_get_key_length(kekctx);
+ if (cipher_length != *kekLength) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_INVALID_KEY_LENGTH);
+ goto err;
+ }
+
+ rv = 1;
+err:
+ EVP_CIPHER_free(kekcipher);
+ return rv;
+}
+
+static int kem_cms_encrypt(CMS_RecipientInfo *ri)
+{
+ uint32_t *kekLength;
+ X509_ALGOR *wrap;
+ X509_ALGOR *kdf;
+ EVP_PKEY_CTX *pctx;
+ EVP_PKEY *pkey;
+ int security_bits;
+ const ASN1_OBJECT *kdf_obj = NULL;
+ unsigned char kemri_x509_algor[OSSL_MAX_ALGORITHM_ID_SIZE];
+ OSSL_PARAM params[2];
+ X509_ALGOR *x509_algor = NULL;
+ EVP_CIPHER_CTX *kekctx;
+ int wrap_nid;
+ int rv = 0;
+
+ if (!ossl_cms_RecipientInfo_kemri_get0_alg(ri, &kekLength, &wrap))
+ goto err;
+
+ kdf = CMS_RecipientInfo_kemri_get0_kdf_alg(ri);
+ if (kdf == NULL)
+ goto err;
+
+ pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+ if (pctx == NULL)
+ goto err;
+
+ pkey = EVP_PKEY_CTX_get0_pkey(pctx);
+ if (pkey == NULL)
+ goto err;
+
+ security_bits = EVP_PKEY_get_security_bits(pkey);
+ if (security_bits == 0)
+ goto err;
+
+ X509_ALGOR_get0(&kdf_obj, NULL, NULL, kdf);
+ if (kdf_obj == NULL || OBJ_obj2nid(kdf_obj) == NID_undef) {
+ /*
+ * If the KDF OID hasn't already been set, then query the provider
+ * for a default KDF.
+ */
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_CMS_KEMRI_KDF_ALGORITHM,
+ kemri_x509_algor, sizeof(kemri_x509_algor));
+ params[1] = OSSL_PARAM_construct_end();
+ if (!EVP_PKEY_get_params(pkey, params))
+ goto err;
+ if (OSSL_PARAM_modified(¶ms[0])) {
+ const unsigned char *p = kemri_x509_algor;
+
+ x509_algor = d2i_X509_ALGOR(NULL, &p, (long)params[0].return_size);
+ if (x509_algor == NULL)
+ goto err;
+ if (!X509_ALGOR_copy(kdf, x509_algor))
+ goto err;
+ } else {
+ if (!X509_ALGOR_set0(kdf, OBJ_nid2obj(NID_HKDF_SHA256), V_ASN1_UNDEF, NULL))
+ return 0;
+ }
+ }
+
+ /* Get wrap NID */
+ kekctx = CMS_RecipientInfo_kemri_get0_ctx(ri);
+ if (kekctx == NULL)
+ goto err;
+ *kekLength = EVP_CIPHER_CTX_get_key_length(kekctx);
+ wrap_nid = EVP_CIPHER_CTX_get_type(kekctx);
+
+ /* Package wrap algorithm in an AlgorithmIdentifier */
+ ASN1_OBJECT_free(wrap->algorithm);
+ ASN1_TYPE_free(wrap->parameter);
+ wrap->algorithm = OBJ_nid2obj(wrap_nid);
+ wrap->parameter = ASN1_TYPE_new();
+ if (wrap->parameter == NULL)
+ goto err;
+ if (EVP_CIPHER_param_to_asn1(kekctx, wrap->parameter) <= 0)
+ goto err;
+ if (ASN1_TYPE_get(wrap->parameter) == NID_undef) {
+ ASN1_TYPE_free(wrap->parameter);
+ wrap->parameter = NULL;
+ }
+
+ rv = 1;
+err:
+ X509_ALGOR_free(x509_algor);
+ return rv;
+}
+
+int ossl_cms_kem_envelope(CMS_RecipientInfo *ri, int decrypt)
+{
+ assert(decrypt == 0 || decrypt == 1);
+
+ if (decrypt == 1)
+ return kem_cms_decrypt(ri);
+
+ if (decrypt == 0)
+ return kem_cms_encrypt(ri);
+
+ ERR_raise(ERR_LIB_CMS, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright 2025 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/core_names.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/x509.h>
+#include "cms_local.h"
+#include "crypto/evp.h"
+#include "internal/sizes.h"
+
+/* KEM Recipient Info (KEMRI) routines */
+
+int ossl_cms_RecipientInfo_kemri_get0_alg(CMS_RecipientInfo *ri,
+ uint32_t **pkekLength,
+ X509_ALGOR **pwrap)
+{
+ if (ri->type != CMS_RECIPINFO_KEM) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
+ return 0;
+ }
+ if (pkekLength)
+ *pkekLength = &ri->d.ori->d.kemri->kekLength;
+ if (pwrap)
+ *pwrap = ri->d.ori->d.kemri->wrap;
+ return 1;
+}
+
+int CMS_RecipientInfo_kemri_cert_cmp(CMS_RecipientInfo *ri, X509 *cert)
+{
+ if (ri->type != CMS_RECIPINFO_KEM) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
+ return -2;
+ }
+ return ossl_cms_SignerIdentifier_cert_cmp(ri->d.ori->d.kemri->rid, cert);
+}
+
+int CMS_RecipientInfo_kemri_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk)
+{
+ EVP_PKEY_CTX *pctx = NULL;
+ CMS_KEMRecipientInfo *kemri;
+
+ if (ri->type != CMS_RECIPINFO_KEM) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
+ return 0;
+ }
+
+ kemri = ri->d.ori->d.kemri;
+
+ EVP_PKEY_CTX_free(kemri->pctx);
+ kemri->pctx = NULL;
+
+ if (pk != NULL) {
+ pctx = EVP_PKEY_CTX_new_from_pkey(ossl_cms_ctx_get0_libctx(kemri->cms_ctx), pk,
+ ossl_cms_ctx_get0_propq(kemri->cms_ctx));
+ if (pctx == NULL || EVP_PKEY_decapsulate_init(pctx, NULL) <= 0)
+ goto err;
+
+ kemri->pctx = pctx;
+ }
+
+ return 1;
+err:
+ EVP_PKEY_CTX_free(pctx);
+ return 0;
+}
+
+/* Initialise a kemri based on passed certificate and key */
+
+int ossl_cms_RecipientInfo_kemri_init(CMS_RecipientInfo *ri, X509 *recip,
+ EVP_PKEY *recipPubKey, unsigned int flags,
+ const CMS_CTX *ctx)
+{
+ CMS_OtherRecipientInfo *ori;
+ CMS_KEMRecipientInfo *kemri;
+ int idtype;
+ X509_PUBKEY *x_pubkey;
+ X509_ALGOR *x_alg;
+
+ ri->d.ori = M_ASN1_new_of(CMS_OtherRecipientInfo);
+ if (ri->d.ori == NULL)
+ return 0;
+ ri->encoded_type = CMS_RECIPINFO_OTHER;
+ ri->type = CMS_RECIPINFO_KEM;
+
+ ori = ri->d.ori;
+ ori->oriType = OBJ_nid2obj(NID_id_smime_ori_kem);
+ if (ori->oriType == NULL)
+ return 0;
+ ori->d.kemri = M_ASN1_new_of(CMS_KEMRecipientInfo);
+ if (ori->d.kemri == NULL)
+ return 0;
+
+ kemri = ori->d.kemri;
+ kemri->version = 0;
+ kemri->cms_ctx = ctx;
+
+ /*
+ * Not a typo: RecipientIdentifier and SignerIdentifier are the same
+ * structure.
+ */
+
+ idtype = (flags & CMS_USE_KEYID) ? CMS_RECIPINFO_KEYIDENTIFIER : CMS_RECIPINFO_ISSUER_SERIAL;
+ if (!ossl_cms_set1_SignerIdentifier(kemri->rid, recip, idtype, ctx))
+ return 0;
+
+ x_pubkey = X509_get_X509_PUBKEY(recip);
+ if (x_pubkey == NULL)
+ return 0;
+ if (!X509_PUBKEY_get0_param(NULL, NULL, NULL, &x_alg, x_pubkey))
+ return 0;
+ if (!X509_ALGOR_copy(kemri->kem, x_alg))
+ return 0;
+
+ kemri->pctx = EVP_PKEY_CTX_new_from_pkey(ossl_cms_ctx_get0_libctx(ctx),
+ recipPubKey,
+ ossl_cms_ctx_get0_propq(ctx));
+ if (kemri->pctx == NULL)
+ return 0;
+ if (EVP_PKEY_encapsulate_init(kemri->pctx, NULL) <= 0)
+ return 0;
+
+ return 1;
+}
+
+EVP_CIPHER_CTX *CMS_RecipientInfo_kemri_get0_ctx(CMS_RecipientInfo *ri)
+{
+ if (ri->type == CMS_RECIPINFO_KEM)
+ return ri->d.ori->d.kemri->ctx;
+ return NULL;
+}
+
+X509_ALGOR *CMS_RecipientInfo_kemri_get0_kdf_alg(CMS_RecipientInfo *ri)
+{
+ if (ri->type == CMS_RECIPINFO_KEM)
+ return ri->d.ori->d.kemri->kdf;
+ return NULL;
+}
+
+int CMS_RecipientInfo_kemri_set_ukm(CMS_RecipientInfo *ri,
+ const unsigned char *ukm,
+ int ukmLength)
+{
+ CMS_KEMRecipientInfo *kemri;
+
+ if (ri->type != CMS_RECIPINFO_KEM) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
+ return 0;
+ }
+
+ if (ukm == NULL && ukmLength != 0) {
+ ERR_raise(ERR_LIB_CMS, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+
+ kemri = ri->d.ori->d.kemri;
+
+ ASN1_OCTET_STRING_free(kemri->ukm);
+ kemri->ukm = ASN1_OCTET_STRING_new();
+ if (kemri->ukm == NULL)
+ return 0;
+ ASN1_OCTET_STRING_set(kemri->ukm, ukm, ukmLength);
+ return 1;
+}
+
+static EVP_KDF_CTX *create_kdf_ctx(CMS_KEMRecipientInfo *kemri)
+{
+ const ASN1_OBJECT *kdf_oid;
+ int ptype;
+ char kdf_alg[OSSL_MAX_NAME_SIZE];
+ EVP_KDF *kdf = NULL;
+ EVP_KDF_CTX *kctx = NULL;
+
+ /*
+ * KDFs with algorithm identifier parameters are not supported yet. To
+ * support this, EVP_KDF_CTX_set_algor_params from
+ * `doc/designs/passing-algorithmidentifier-parameters.md` needs to be
+ * implemented.
+ */
+ X509_ALGOR_get0(&kdf_oid, &ptype, NULL, kemri->kdf);
+ if (ptype != V_ASN1_UNDEF && ptype != V_ASN1_NULL) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_KDF_ALGORITHM);
+ goto err;
+ }
+ if (OBJ_obj2txt(kdf_alg, sizeof(kdf_alg), kdf_oid, 1) < 0)
+ goto err;
+
+ kdf = EVP_KDF_fetch(ossl_cms_ctx_get0_libctx(kemri->cms_ctx), kdf_alg,
+ ossl_cms_ctx_get0_propq(kemri->cms_ctx));
+ if (kdf == NULL)
+ goto err;
+
+ kctx = EVP_KDF_CTX_new(kdf);
+err:
+ EVP_KDF_free(kdf);
+ return kctx;
+}
+
+static int kdf_derive(unsigned char *kek, size_t keklen,
+ const unsigned char *ss, size_t sslen,
+ CMS_KEMRecipientInfo *kemri)
+{
+ EVP_KDF_CTX *kctx = NULL;
+ OSSL_PARAM params[3];
+ unsigned char *infoder = NULL;
+ int infolen = 0;
+ int rv = 0;
+
+ infolen = CMS_CMSORIforKEMOtherInfo_encode(&infoder, kemri->wrap, kemri->ukm,
+ kemri->kekLength);
+ if (infolen <= 0)
+ goto err;
+
+ kctx = create_kdf_ctx(kemri);
+ if (kctx == NULL)
+ goto err;
+
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+ (unsigned char *)ss, sslen);
+ params[1] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
+ (char *)infoder, infolen);
+ params[2] = OSSL_PARAM_construct_end();
+
+ if (EVP_KDF_derive(kctx, kek, keklen, params) <= 0)
+ goto err;
+
+ rv = 1;
+err:
+ OPENSSL_free(infoder);
+ EVP_KDF_CTX_free(kctx);
+
+ return rv;
+}
+
+/*
+ * Derive KEK and decrypt/encrypt with it to produce either the original CEK
+ * or the encrypted CEK.
+ */
+
+static int cms_kek_cipher(unsigned char **pout, size_t *poutlen,
+ const unsigned char *ss, size_t sslen,
+ const unsigned char *in, size_t inlen,
+ CMS_KEMRecipientInfo *kemri, int enc)
+{
+ /* Key encryption key */
+ unsigned char kek[EVP_MAX_KEY_LENGTH];
+ size_t keklen = kemri->kekLength;
+ unsigned char *out = NULL;
+ int outlen = 0;
+ int rv = 0;
+
+ if (keklen > sizeof(kek)) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_INVALID_KEY_LENGTH);
+ goto err;
+ }
+
+ if (!kdf_derive(kek, keklen, ss, sslen, kemri))
+ goto err;
+
+ /* Set KEK in context */
+ if (!EVP_CipherInit_ex(kemri->ctx, NULL, NULL, kek, NULL, enc))
+ goto err;
+ /* obtain output length of ciphered key */
+ if (!EVP_CipherUpdate(kemri->ctx, NULL, &outlen, in, (int)inlen))
+ goto err;
+ out = OPENSSL_malloc(outlen);
+ if (out == NULL)
+ goto err;
+ if (!EVP_CipherUpdate(kemri->ctx, out, &outlen, in, (int)inlen))
+ goto err;
+ *pout = out;
+ out = NULL;
+ *poutlen = (size_t)outlen;
+
+ rv = 1;
+err:
+ OPENSSL_free(out);
+ OPENSSL_cleanse(kek, keklen);
+ EVP_CIPHER_CTX_reset(kemri->ctx);
+ EVP_PKEY_CTX_free(kemri->pctx);
+ kemri->pctx = NULL;
+ return rv;
+}
+
+/* Encrypt content key in KEM recipient info */
+
+int ossl_cms_RecipientInfo_kemri_encrypt(const CMS_ContentInfo *cms,
+ CMS_RecipientInfo *ri)
+{
+ CMS_KEMRecipientInfo *kemri;
+ CMS_EncryptedContentInfo *ec;
+ unsigned char *kem_ct = NULL;
+ size_t kem_ct_len;
+ unsigned char *kem_secret = NULL;
+ size_t kem_secret_len = 0;
+ unsigned char *enckey;
+ size_t enckeylen;
+ int rv = 0;
+
+ if (ri->type != CMS_RECIPINFO_KEM) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
+ return 0;
+ }
+
+ kemri = ri->d.ori->d.kemri;
+
+ ec = ossl_cms_get0_env_enc_content(cms);
+ /* Initialise wrap algorithm parameters */
+ if (!ossl_cms_RecipientInfo_wrap_init(ri, ec->cipher))
+ return 0;
+
+ /* Initialise KDF algorithm */
+ if (!ossl_cms_env_asn1_ctrl(ri, 0))
+ return 0;
+
+ if (EVP_PKEY_encapsulate(kemri->pctx, NULL, &kem_ct_len, NULL, &kem_secret_len) <= 0)
+ return 0;
+ kem_ct = OPENSSL_malloc(kem_ct_len);
+ kem_secret = OPENSSL_malloc(kem_secret_len);
+ if (kem_ct == NULL || kem_secret == NULL)
+ goto err;
+
+ if (EVP_PKEY_encapsulate(kemri->pctx, kem_ct, &kem_ct_len, kem_secret, &kem_secret_len) <= 0)
+ goto err;
+
+ ASN1_STRING_set0(kemri->kemct, kem_ct, (int)kem_ct_len);
+ kem_ct = NULL;
+
+ if (!cms_kek_cipher(&enckey, &enckeylen, kem_secret, kem_secret_len, ec->key, ec->keylen,
+ kemri, 1))
+ goto err;
+ ASN1_STRING_set0(kemri->encryptedKey, enckey, (int)enckeylen);
+
+ rv = 1;
+err:
+ OPENSSL_free(kem_ct);
+ OPENSSL_clear_free(kem_secret, kem_secret_len);
+ return rv;
+}
+
+int ossl_cms_RecipientInfo_kemri_decrypt(const CMS_ContentInfo *cms,
+ CMS_RecipientInfo *ri)
+{
+ CMS_KEMRecipientInfo *kemri;
+ CMS_EncryptedContentInfo *ec;
+ const unsigned char *kem_ct = NULL;
+ size_t kem_ct_len;
+ unsigned char *kem_secret = NULL;
+ size_t kem_secret_len = 0;
+ unsigned char *enckey = NULL;
+ size_t enckeylen;
+ unsigned char *cek = NULL;
+ size_t ceklen;
+ int ret = 0;
+
+ if (ri->type != CMS_RECIPINFO_KEM) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
+ return 0;
+ }
+
+ kemri = ri->d.ori->d.kemri;
+
+ ec = ossl_cms_get0_env_enc_content(cms);
+
+ if (kemri->pctx == NULL) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_NO_PRIVATE_KEY);
+ return 0;
+ }
+
+ /* Setup all parameters to derive KEK */
+ if (!ossl_cms_env_asn1_ctrl(ri, 1))
+ goto err;
+
+ kem_ct = ASN1_STRING_get0_data(kemri->kemct);
+ kem_ct_len = ASN1_STRING_length(kemri->kemct);
+
+ if (EVP_PKEY_decapsulate(kemri->pctx, NULL, &kem_secret_len, kem_ct, kem_ct_len) <= 0)
+ return 0;
+ kem_secret = OPENSSL_malloc(kem_secret_len);
+ if (kem_secret == NULL)
+ goto err;
+
+ if (EVP_PKEY_decapsulate(kemri->pctx, kem_secret, &kem_secret_len, kem_ct, kem_ct_len) <= 0)
+ goto err;
+
+ /* Attempt to decrypt CEK */
+ enckeylen = kemri->encryptedKey->length;
+ enckey = kemri->encryptedKey->data;
+ if (!cms_kek_cipher(&cek, &ceklen, kem_secret, kem_secret_len, enckey, enckeylen, kemri, 0))
+ goto err;
+ ec = ossl_cms_get0_env_enc_content(cms);
+ OPENSSL_clear_free(ec->key, ec->keylen);
+ ec->key = cek;
+ ec->keylen = ceklen;
+
+ ret = 1;
+err:
+ OPENSSL_clear_free(kem_secret, kem_secret_len);
+ return ret;
+}
/*
- * Copyright 2008-2024 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2008-2025 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
typedef struct CMS_KEKRecipientInfo_st CMS_KEKRecipientInfo;
typedef struct CMS_PasswordRecipientInfo_st CMS_PasswordRecipientInfo;
typedef struct CMS_OtherRecipientInfo_st CMS_OtherRecipientInfo;
+typedef struct CMS_KEMRecipientInfo_st CMS_KEMRecipientInfo;
typedef struct CMS_ReceiptsFrom_st CMS_ReceiptsFrom;
typedef struct CMS_CTX_st CMS_CTX;
};
struct CMS_RecipientInfo_st {
- int type;
+ /*
+ * Type which the RecipientInfo is encoded with. OtherRecipientInfo
+ * encompasses different types, specified by 'type' below.
+ */
+ int encoded_type;
union {
CMS_KeyTransRecipientInfo *ktri;
CMS_KeyAgreeRecipientInfo *kari;
CMS_PasswordRecipientInfo *pwri;
CMS_OtherRecipientInfo *ori;
} d;
+ /* internal type, including ORI types */
+ int type;
};
typedef CMS_SignerIdentifier CMS_RecipientIdentifier;
struct CMS_OtherRecipientInfo_st {
ASN1_OBJECT *oriType;
- ASN1_TYPE *oriValue;
+ union {
+ /* NID_id_smime_ori_kem */
+ CMS_KEMRecipientInfo *kemri;
+ /* anything else */
+ ASN1_TYPE *other;
+ } d;
+};
+
+struct CMS_KEMRecipientInfo_st {
+ int32_t version;
+ CMS_RecipientIdentifier *rid;
+ X509_ALGOR *kem;
+ ASN1_OCTET_STRING *kemct;
+ X509_ALGOR *kdf;
+ uint32_t kekLength;
+ ASN1_OCTET_STRING *ukm;
+ X509_ALGOR *wrap;
+ ASN1_OCTET_STRING *encryptedKey;
+ /* Public key context associated with current operation */
+ EVP_PKEY_CTX *pctx;
+ /* Cipher context for CEK wrapping */
+ EVP_CIPHER_CTX *ctx;
+ const CMS_CTX *cms_ctx;
};
struct CMS_DigestedData_st {
int ossl_cms_pkey_is_ri_type_supported(EVP_PKEY *pk, int ri_type);
void ossl_cms_RecipientInfos_set_cmsctx(CMS_ContentInfo *cms);
+int ossl_cms_RecipientInfo_wrap_init(CMS_RecipientInfo *ri, const EVP_CIPHER *cipher);
/* KARI routines */
int ossl_cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip,
int ossl_cms_RecipientInfo_kari_encrypt(const CMS_ContentInfo *cms,
CMS_RecipientInfo *ri);
+/* KEMRI routines */
+int ossl_cms_RecipientInfo_kemri_get0_alg(CMS_RecipientInfo *ri,
+ uint32_t **pkekLength,
+ X509_ALGOR **pwrap);
+int ossl_cms_RecipientInfo_kemri_init(CMS_RecipientInfo *ri, X509 *recip,
+ EVP_PKEY *recipPubKey, unsigned int flags,
+ const CMS_CTX *ctx);
+int ossl_cms_RecipientInfo_kemri_encrypt(const CMS_ContentInfo *cms,
+ CMS_RecipientInfo *ri);
+int ossl_cms_RecipientInfo_kemri_decrypt(const CMS_ContentInfo *cms,
+ CMS_RecipientInfo *ri);
+int CMS_CMSORIforKEMOtherInfo_encode(unsigned char **pder, X509_ALGOR *wrap,
+ ASN1_OCTET_STRING *ukm, int keylen);
+
/* PWRI routines */
int ossl_cms_RecipientInfo_pwri_crypt(const CMS_ContentInfo *cms,
CMS_RecipientInfo *ri, int en_de);
int ossl_cms_ecdh_envelope(CMS_RecipientInfo *ri, int decrypt);
int ossl_cms_rsa_envelope(CMS_RecipientInfo *ri, int decrypt);
int ossl_cms_rsa_sign(CMS_SignerInfo *si, int verify);
+int ossl_cms_kem_envelope(CMS_RecipientInfo *ri, int decrypt);
int ossl_cms_get1_certs_ex(CMS_ContentInfo *cms, STACK_OF(X509) **certs);
int ossl_cms_get1_crls_ex(CMS_ContentInfo *cms, STACK_OF(X509_CRL) **crls);
DECLARE_ASN1_ITEM(CMS_EnvelopedData)
DECLARE_ASN1_ITEM(CMS_AuthEnvelopedData)
DECLARE_ASN1_ITEM(CMS_KEKRecipientInfo)
+DECLARE_ASN1_ITEM(CMS_KEMRecipientInfo)
DECLARE_ASN1_ITEM(CMS_KeyAgreeRecipientInfo)
DECLARE_ASN1_ITEM(CMS_KeyTransRecipientInfo)
DECLARE_ASN1_ITEM(CMS_OriginatorPublicKey)
DECLARE_ASN1_ITEM(CMS_OtherKeyAttribute)
+DECLARE_ASN1_ITEM(CMS_OtherRecipientInfo)
DECLARE_ASN1_ITEM(CMS_Receipt)
DECLARE_ASN1_ITEM(CMS_ReceiptRequest)
DECLARE_ASN1_ITEM(CMS_RecipientEncryptedKey)
ERR_raise(ERR_LIB_CMS, ERR_R_ASN1_LIB);
goto err;
}
- ri->type = CMS_RECIPINFO_PASS;
+ ri->encoded_type = ri->type = CMS_RECIPINFO_PASS;
pwri = ri->d.pwri;
pwri->cms_ctx = cms_ctx;
return 1;
if (r < 0)
return 0;
+ } else if (ri_type == CMS_RECIPINFO_KEM) {
+ if (cert == NULL || !CMS_RecipientInfo_kemri_cert_cmp(ri, cert)) {
+ CMS_RecipientInfo_kemri_set0_pkey(ri, pk);
+ r = CMS_RecipientInfo_decrypt(cms, ri);
+ CMS_RecipientInfo_kemri_set0_pkey(ri, NULL);
+ if (cert != NULL || r > 0)
+ return r;
+ }
}
/* If we have a cert, try matching RecipientInfo, else try them all */
else if (cert == NULL || !CMS_RecipientInfo_ktri_cert_cmp(ri, cert)) {
CMS_R_NOT_A_SIGNED_RECEIPT:165:not a signed receipt
CMS_R_NOT_ENCRYPTED_DATA:122:not encrypted data
CMS_R_NOT_KEK:123:not kek
+CMS_R_NOT_KEM:197:not kem
CMS_R_NOT_KEY_AGREEMENT:181:not key agreement
CMS_R_NOT_KEY_TRANSPORT:124:not key transport
CMS_R_NOT_PWRI:177:not pwri
CMS_R_UNKNOWN_CIPHER:148:unknown cipher
CMS_R_UNKNOWN_DIGEST_ALGORITHM:149:unknown digest algorithm
CMS_R_UNKNOWN_ID:150:unknown id
+CMS_R_UNKNOWN_KDF_ALGORITHM:198:unknown kdf algorithm
CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM:151:unsupported compression algorithm
CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM:194:\
unsupported content encryption algorithm
CMS_R_UNSUPPORTED_CONTENT_TYPE:152:unsupported content type
CMS_R_UNSUPPORTED_ENCRYPTION_TYPE:192:unsupported encryption type
+CMS_R_UNSUPPORTED_KDF_ALGORITHM:199:unsupported kdf algorithm
CMS_R_UNSUPPORTED_KEK_ALGORITHM:153:unsupported kek algorithm
CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM:179:\
unsupported key encryption algorithm
*/
/* Serialized OID's */
-static const unsigned char so[9550] = {
+static const unsigned char so[9571] = {
0x2A,0x86,0x48,0x86,0xF7,0x0D, /* [ 0] OBJ_rsadsi */
0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, /* [ 6] OBJ_pkcs */
0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x02, /* [ 13] OBJ_md2 */
0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x1C, /* [ 9516] OBJ_HKDF_SHA256 */
0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x1D, /* [ 9527] OBJ_HKDF_SHA384 */
0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x03,0x1E, /* [ 9538] OBJ_HKDF_SHA512 */
+ 0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x0D, /* [ 9549] OBJ_id_smime_ori */
+ 0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x0D,0x03, /* [ 9559] OBJ_id_smime_ori_kem */
};
-#define NUM_NID 1499
+#define NUM_NID 1501
static const ASN1_OBJECT nid_objs[NUM_NID] = {
{"UNDEF", "undefined", NID_undef},
{"rsadsi", "RSA Data Security, Inc.", NID_rsadsi, 6, &so[0]},
{"id-alg-hkdf-with-sha256", "HKDF-SHA256", NID_HKDF_SHA256, 11, &so[9516]},
{"id-alg-hkdf-with-sha384", "HKDF-SHA384", NID_HKDF_SHA384, 11, &so[9527]},
{"id-alg-hkdf-with-sha512", "HKDF-SHA512", NID_HKDF_SHA512, 11, &so[9538]},
+ {"id-smime-ori", "id-smime-ori", NID_id_smime_ori, 10, &so[9549]},
+ {"id-smime-ori-kem", "id-smime-ori-kem", NID_id_smime_ori_kem, 11, &so[9559]},
};
-#define NUM_SN 1490
+#define NUM_SN 1492
static const unsigned int sn_objs[NUM_SN] = {
364, /* "AD_DVCS" */
419, /* "AES-128-CBC" */
201, /* "id-smime-mod-ets-eSignature-97" */
199, /* "id-smime-mod-msg-v3" */
198, /* "id-smime-mod-oid" */
+ 1499, /* "id-smime-ori" */
+ 1500, /* "id-smime-ori-kem" */
194, /* "id-smime-spq" */
250, /* "id-smime-spq-ets-sqt-unotice" */
249, /* "id-smime-spq-ets-sqt-uri" */
1289, /* "zstd" */
};
-#define NUM_LN 1490
+#define NUM_LN 1492
static const unsigned int ln_objs[NUM_LN] = {
363, /* "AD Time Stamping" */
405, /* "ANSI X9.62" */
201, /* "id-smime-mod-ets-eSignature-97" */
199, /* "id-smime-mod-msg-v3" */
198, /* "id-smime-mod-oid" */
+ 1499, /* "id-smime-ori" */
+ 1500, /* "id-smime-ori-kem" */
194, /* "id-smime-spq" */
250, /* "id-smime-spq-ets-sqt-unotice" */
249, /* "id-smime-spq-ets-sqt-uri" */
125, /* "zlib compression" */
};
-#define NUM_OBJ 1347
+#define NUM_OBJ 1349
static const unsigned int obj_objs[NUM_OBJ] = {
0, /* OBJ_undef 0 */
181, /* OBJ_iso 1 */
193, /* OBJ_id_smime_cd 1 2 840 113549 1 9 16 4 */
194, /* OBJ_id_smime_spq 1 2 840 113549 1 9 16 5 */
195, /* OBJ_id_smime_cti 1 2 840 113549 1 9 16 6 */
+ 1499, /* OBJ_id_smime_ori 1 2 840 113549 1 9 16 13 */
158, /* OBJ_x509Certificate 1 2 840 113549 1 9 22 1 */
159, /* OBJ_sdsiCertificate 1 2 840 113549 1 9 22 2 */
160, /* OBJ_x509Crl 1 2 840 113549 1 9 23 1 */
254, /* OBJ_id_smime_cti_ets_proofOfSender 1 2 840 113549 1 9 16 6 4 */
255, /* OBJ_id_smime_cti_ets_proofOfApproval 1 2 840 113549 1 9 16 6 5 */
256, /* OBJ_id_smime_cti_ets_proofOfCreation 1 2 840 113549 1 9 16 6 6 */
+ 1500, /* OBJ_id_smime_ori_kem 1 2 840 113549 1 9 16 13 3 */
150, /* OBJ_keyBag 1 2 840 113549 1 12 10 1 1 */
151, /* OBJ_pkcs8ShroudedKeyBag 1 2 840 113549 1 12 10 1 2 */
152, /* OBJ_certBag 1 2 840 113549 1 12 10 1 3 */
HKDF_SHA256 1496
HKDF_SHA384 1497
HKDF_SHA512 1498
+id_smime_ori 1499
+id_smime_ori_kem 1500
SMIME 4 : id-smime-cd
SMIME 5 : id-smime-spq
SMIME 6 : id-smime-cti
+SMIME 13 : id-smime-ori
# S/MIME Modules
id-smime-mod 1 : id-smime-mod-cms
id-smime-cti 5 : id-smime-cti-ets-proofOfApproval
id-smime-cti 6 : id-smime-cti-ets-proofOfCreation
+# S/MIME OtherRecipientInfo Type Identifier
+id-smime-ori 3 : id-smime-ori-kem
+
pkcs9 20 : : friendlyName
pkcs9 21 : : localKeyID
!Alias ms-corp 1 3 6 1 4 1 311
[B<-aes256-wrap>]
[B<-des3-wrap>]
[B<-debug_decrypt>]
+[B<-recip_kdf> I<kdf>]
+[B<-recip_ukm> I<ukm>]
Signing options:
This option sets the B<CMS_DEBUG_DECRYPT> flag. This option should be used
with caution: see the notes section below.
+=item B<-recip_kdf>
+
+This option sets the KDF used to generate the key encryption key in the
+B<KEMRecipientInfo> type. Any KDF that takes B<OSSL_KDF_PARAM_KEY> and
+B<OSSL_KDF_PARAM_INFO> parameters and is otherwise fully defined by its name or
+OID can be used, for example B<HKDF-SHA256>.
+
+=item B<-recip_ukm>
+
+This option sets the B<KEMRecipientInfo> type's optional user keying material (UKM)
+in hexadecimal form. The UKM will be encoded, along with other information, into the
+B<OSSL_KDF_PARAM_INFO> parameter of the B<KEMRecipientInfo> type's KDF.
+
=back
=head2 Signing options
The B<-digest> option was added in OpenSSL 3.2.
+The B<-recip_kdf> and B<-recip_ukm> options were added in OpenSSL 3.6.
+
=head1 COPYRIGHT
Copyright 2008-2025 The OpenSSL Project Authors. All Rights Reserved.
method of providing of the shared key is in use.
CMS_add1_recipient_cert() adds recipient B<recip> to CMS_ContentInfo enveloped
-data structure B<cms> as a KeyTransRecipientInfo structure.
+data structure B<cms> as a KeyTransRecipientInfo or KEMRecipientInfo structure,
+or as a KeyAgreeRecipientInfo structure with an ephemeral key.
CMS_add0_recipient_key() adds symmetric key B<key> of length B<keylen> using
wrapping algorithm B<nid>, identifier B<id> of length B<idlen> and optional
The recipients specified in B<certs> use a CMS KeyTransRecipientInfo info
structure. KEKRecipientInfo is also supported using the flag B<CMS_PARTIAL>
-and CMS_add0_recipient_key().
+and CMS_add0_recipient_key(). KEMRecipientInfo is also supported using the
+flag B<CMS_PARTIAL> and CMS_add1_recipient().
The parameter B<certs> may be NULL if B<CMS_PARTIAL> is set and recipients
-added later using CMS_add1_recipient_cert() or CMS_add0_recipient_key().
+are added later using CMS_add1_recipient() or CMS_add1_recipient_cert() with
+CMS_add0_recipient_key().
CMS_encrypt() is similar to CMS_encrypt_ex() but uses default values
of NULL for the library context I<libctx> and the property query I<propq>.
=head1 COPYRIGHT
-Copyright 2008-2020 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2008-2025 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
CMS_RecipientInfo_ktri_get0_signer_id, CMS_RecipientInfo_ktri_cert_cmp,
CMS_RecipientInfo_set0_pkey, CMS_RecipientInfo_kekri_get0_id,
CMS_RecipientInfo_kari_set0_pkey_and_peer,
-CMS_RecipientInfo_kari_set0_pkey,
+CMS_RecipientInfo_kari_set0_pkey, CMS_RecipientInfo_kari_get0_ctx,
CMS_RecipientInfo_kekri_id_cmp, CMS_RecipientInfo_set0_key,
-CMS_RecipientInfo_decrypt, CMS_RecipientInfo_encrypt
+CMS_RecipientInfo_kemri_cert_cmp, CMS_RecipientInfo_kemri_set0_pkey,
+CMS_RecipientInfo_kemri_get0_ctx, CMS_RecipientInfo_kemri_get0_kdf_alg,
+CMS_RecipientInfo_kemri_set_ukm, CMS_RecipientInfo_decrypt,
+CMS_RecipientInfo_encrypt
- CMS envelopedData RecipientInfo routines
=head1 SYNOPSIS
int CMS_RecipientInfo_kari_set0_pkey_and_peer(CMS_RecipientInfo *ri,
EVP_PKEY *pk, X509 *peer);
int CMS_RecipientInfo_kari_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk);
+ EVP_CIPHER_CTX *CMS_RecipientInfo_kari_get0_ctx(CMS_RecipientInfo *ri);
int CMS_RecipientInfo_kekri_get0_id(CMS_RecipientInfo *ri, X509_ALGOR **palg,
ASN1_OCTET_STRING **pid,
ASN1_GENERALIZEDTIME **pdate,
const unsigned char *id, size_t idlen);
int CMS_RecipientInfo_set0_key(CMS_RecipientInfo *ri,
unsigned char *key, size_t keylen);
+ int CMS_RecipientInfo_kemri_cert_cmp(CMS_RecipientInfo *ri, X509 *cert);
+ int CMS_RecipientInfo_kemri_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk);
+ EVP_CIPHER_CTX *CMS_RecipientInfo_kemri_get0_ctx(CMS_RecipientInfo *ri);
+ X509_ALGOR *CMS_RecipientInfo_kemri_get0_kdf_alg(CMS_RecipientInfo *ri);
+ int CMS_RecipientInfo_kemri_set_ukm(CMS_RecipientInfo *ri,
+ const unsigned char *ukm,
+ int ukmLength);
int CMS_RecipientInfo_decrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri);
int CMS_RecipientInfo_encrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri);
CMS_RecipientInfo_type() returns the type of CMS_RecipientInfo structure B<ri>.
It will currently return CMS_RECIPINFO_TRANS, CMS_RECIPINFO_AGREE,
-CMS_RECIPINFO_KEK, CMS_RECIPINFO_PASS, or CMS_RECIPINFO_OTHER.
+CMS_RECIPINFO_KEK, CMS_RECIPINFO_PASS, CMS_RECIPINFO_KEM, or
+CMS_RECIPINFO_OTHER.
CMS_RecipientInfo_ktri_get0_signer_id() retrieves the certificate recipient
identifier associated with a specific CMS_RecipientInfo structure B<ri>, which
CMS_RecipientInfo_kari_set0_pkey() associates the private key B<pkey> with the
CMS_RecipientInfo structure B<ri>, which must be of type CMS_RECIPINFO_AGREE.
+CMS_RecipientInfo_kari_get0_ctx() returns the EVP_CIPHER_CTX for the key
+encryption key, allowing the caller to specify the key wrap cipher. The
+CMS_RecipientInfo structure B<ri> must be of type CMS_RECIPINFO_AGREE.
+
CMS_RecipientInfo_kekri_get0_id() retrieves the key information from the
CMS_RecipientInfo structure B<ri> which must be of type CMS_RECIPINFO_KEK. Any
of the remaining parameters can be NULL if the application is not interested in
B<keylen> with the CMS_RecipientInfo structure B<ri>, which must be of type
CMS_RECIPINFO_KEK.
+CMS_RecipientInfo_kemri_cert_cmp() compares the certificate B<cert> against the
+CMS_RecipientInfo structure B<ri>, which must be of type CMS_RECIPINFO_KEM.
+It returns zero if the comparison is successful and non zero if not.
+
+CMS_RecipientInfo_kemri_set0_pkey() associates the private key B<pkey> with the
+CMS_RecipientInfo structure B<ri>, which must be of type CMS_RECIPINFO_KEM.
+
+CMS_RecipientInfo_kemri_get0_ctx() returns the EVP_CIPHER_CTX for the key
+encryption key, allowing the caller to specify the key wrap cipher. The
+CMS_RecipientInfo structure B<ri> must be of type CMS_RECIPINFO_KEM.
+
+CMS_RecipientInfo_kemri_get0_kdf_alg() returns the X509_ALGOR for the
+RecipientInfo's KDF, allowing the caller to specify the KDF algorithm. The
+CMS_RecipientInfo structure B<ri> must be of type CMS_RECIPINFO_KEM. If the
+caller doesn't specify a KDF algorithm, B<HKDF-SHA256> will be used.
+
+CMS_RecipientInfo_kemri_set_ukm() sets the RecipientInfo's optional user
+keying material (UKM). The UKM is encoded, along with other information, into
+the B<OSSL_KDF_PARAM_INFO> parameter of the RecipientInfo's KDF. The
+CMS_RecipientInfo structure B<ri> must be of type CMS_RECIPINFO_KEM.
+
CMS_RecipientInfo_decrypt() attempts to decrypt CMS_RecipientInfo structure
B<ri> in structure B<cms>. A key must have been associated with the structure
first.
an error occurs.
CMS_RecipientInfo_ktri_get0_signer_id(), CMS_RecipientInfo_set0_pkey(),
-CMS_RecipientInfo_kekri_get0_id(), CMS_RecipientInfo_set0_key() and
-CMS_RecipientInfo_decrypt() return 1 for success or 0 if an error occurs.
-CMS_RecipientInfo_encrypt() return 1 for success or 0 if an error occurs.
+CMS_RecipientInfo_kekri_get0_id(), CMS_RecipientInfo_set0_key(),
+CMS_RecipientInfo_kemri_set0_pkey(), CMS_RecipientInfo_kemri_set_ukm(),
+CMS_RecipientInfo_decrypt() and CMS_RecipientInfo_encrypt() return 1 for
+success or 0 if an error occurs.
+
+CMS_RecipientInfo_ktri_cert_cmp(), CMS_RecipientInfo_kemri_cert_cmp() and
+CMS_RecipientInfo_kekri_cmp() return 0 for a successful comparison and non zero
+otherwise.
-CMS_RecipientInfo_ktri_cert_cmp() and CMS_RecipientInfo_kekri_cmp() return 0
-for a successful comparison and non zero otherwise.
+CMS_RecipientInfo_kemri_get0_ctx() and CMS_RecipientInfo_kari_get0_ctx return
+the RecipientInfo's EVP_CIPHER_CTX or NULL if an error occurred.
+
+CMS_RecipientInfo_kemri_get0_kdf_alg() returns the RecipientInfo's KDF's
+X509_ALGOR or NULL if an error occurred.
Any error can be obtained from L<ERR_get_error(3)>.
B<CMS_RecipientInfo_kari_set0_pkey_and_peer> and B<CMS_RecipientInfo_kari_set0_pkey>
were added in OpenSSL 3.0.
+B<CMS_RecipientInfo_kemri_cert_cmp>, B<CMS_RecipientInfo_kemri_set0_pkey>,
+B<CMS_RecipientInfo_kemri_get0_ctx>, B<CMS_RecipientInfo_kemri_get0_kdf_alg>
+and B<CMS_RecipientInfo_kemri_set_ukm> were added in OpenSSL 3.6.
+
=head1 COPYRIGHT
-Copyright 2008-2020 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2008-2025 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
with the corresponding signature operation, but may be specified as an
option.
+=item "ri-type" (B<OSSL_PKEY_PARAM_CMS_RI_TYPE>) <integer>
+
+The value should be the CMS RecipientInfo type for the given key, for example
+B<CMS_RECIPINFO_KEM> or B<CMS_RECIPINFO_AGREE>.
+The value that can be given through this parameter is found in
+F<< <openssl/cms.h> >>, with the macros having names starting with
+C<CMS_RECIPINFO_>.
+
+CMS will query this parameter first to determine the RecipientInfo type. If
+this parameter is not filled in, CMS will check for known key types and map
+them to the appropriate RecipientInfo type. Otherwise, CMS will default to
+using B<CMS_RECIPINFO_TRANS>.
+
+=item "kemri-kdf-alg" (B<OSSL_PKEY_PARAM_CMS_KEMRI_KDF_ALGORITHM>) <UTF8 string>
+
+The value should be the DER-encoded X509_ALGOR for the default KDF for this key
+if it supports the KEMRecipientInfo (B<CMS_RECIPINFO_KEM>) type.
+
+Any KDF that takes B<OSSL_KDF_PARAM_KEY> and B<OSSL_KDF_PARAM_INFO> parameters
+and is otherwise fully defined by its OID can be used, for example B<HKDF-SHA256>.
+
+If B<OSSL_PKEY_PARAM_CMS_KEMRI_KDF_ALGORITHM> is not implemented, B<HKDF-SHA256>
+will be used as the default KDF.
+
=back
The OpenSSL FIPS provider also supports the following parameters:
OBJ_HKDF_SHA256="\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x03\x1C"
OBJ_HKDF_SHA384="\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x03\x1D"
OBJ_HKDF_SHA512="\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x03\x1E"
+OBJ_id_smime_ori="\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x0D"
+OBJ_id_smime_ori_kem="\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x0D\x03"
# define CMS_RECIPINFO_KEK 2
# define CMS_RECIPINFO_PASS 3
# define CMS_RECIPINFO_OTHER 4
+# define CMS_RECIPINFO_KEM 5
/* S/MIME related flags */
int CMS_SharedInfo_encode(unsigned char **pder, X509_ALGOR *kekalg,
ASN1_OCTET_STRING *ukm, int keylen);
+int CMS_RecipientInfo_kemri_cert_cmp(CMS_RecipientInfo *ri, X509 *cert);
+int CMS_RecipientInfo_kemri_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk);
+EVP_CIPHER_CTX *CMS_RecipientInfo_kemri_get0_ctx(CMS_RecipientInfo *ri);
+X509_ALGOR *CMS_RecipientInfo_kemri_get0_kdf_alg(CMS_RecipientInfo *ri);
+int CMS_RecipientInfo_kemri_set_ukm(CMS_RecipientInfo *ri,
+ const unsigned char *ukm,
+ int ukmLength);
+
/* Backward compatibility for spelling errors. */
# define CMS_R_UNKNOWN_DIGEST_ALGORITM CMS_R_UNKNOWN_DIGEST_ALGORITHM
# define CMS_R_UNSUPPORTED_RECPIENTINFO_TYPE \
# define CMS_R_NOT_A_SIGNED_RECEIPT 165
# define CMS_R_NOT_ENCRYPTED_DATA 122
# define CMS_R_NOT_KEK 123
+# define CMS_R_NOT_KEM 197
# define CMS_R_NOT_KEY_AGREEMENT 181
# define CMS_R_NOT_KEY_TRANSPORT 124
# define CMS_R_NOT_PWRI 177
# define CMS_R_UNKNOWN_CIPHER 148
# define CMS_R_UNKNOWN_DIGEST_ALGORITHM 149
# define CMS_R_UNKNOWN_ID 150
+# define CMS_R_UNKNOWN_KDF_ALGORITHM 198
# define CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM 151
# define CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM 194
# define CMS_R_UNSUPPORTED_CONTENT_TYPE 152
# define CMS_R_UNSUPPORTED_ENCRYPTION_TYPE 192
+# define CMS_R_UNSUPPORTED_KDF_ALGORITHM 199
# define CMS_R_UNSUPPORTED_KEK_ALGORITHM 153
# define CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM 179
# define CMS_R_UNSUPPORTED_LABEL_SOURCE 193
#define NID_id_smime_cti 195
#define OBJ_id_smime_cti OBJ_SMIME,6L
+#define SN_id_smime_ori "id-smime-ori"
+#define NID_id_smime_ori 1499
+#define OBJ_id_smime_ori OBJ_SMIME,13L
+
#define SN_id_smime_mod_cms "id-smime-mod-cms"
#define NID_id_smime_mod_cms 196
#define OBJ_id_smime_mod_cms OBJ_id_smime_mod,1L
#define NID_id_smime_cti_ets_proofOfCreation 256
#define OBJ_id_smime_cti_ets_proofOfCreation OBJ_id_smime_cti,6L
+#define SN_id_smime_ori_kem "id-smime-ori-kem"
+#define NID_id_smime_ori_kem 1500
+#define OBJ_id_smime_ori_kem OBJ_id_smime_ori,3L
+
#define LN_friendlyName "friendlyName"
#define NID_friendlyName 156
#define OBJ_friendlyName OBJ_pkcs9,20L
--- /dev/null
+-- Copyright 2025 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
+
+-- -------------------------------------------------------------------
+-- Taken from https://datatracker.ietf.org/doc/rfc8619/
+
+id-smime OBJECT IDENTIFIER ::= { 1 2 840 113549 1 9 16 }
+id-alg OBJECT IDENTIFIER ::= { id-smime 3 }
+
+id-alg-hkdf-with-sha256 OBJECT IDENTIFIER ::= { id-alg 28 }
+id-alg-hkdf-with-sha384 OBJECT IDENTIFIER ::= { id-alg 29 }
+id-alg-hkdf-with-sha512 OBJECT IDENTIFIER ::= { id-alg 30 }
DEPEND[$DER_SLH_DSA_H]=oids_to_c.pm SLH_DSA.asn1
ENDIF
+#----- HKDF
+$DER_HKDF_H=$INCDIR/der_hkdf.h
+$DER_HKDF_GEN=der_hkdf_gen.c
+
+GENERATE[$DER_HKDF_GEN]=der_hkdf_gen.c.in
+DEPEND[$DER_HKDF_GEN]=oids_to_c.pm HKDF.asn1
+
+DEPEND[${DER_HKDF_GEN/.c/.o}]=$DER_HKDF_H
+GENERATE[$DER_HKDF_H]=$INCDIR/der_hkdf.h.in
+DEPEND[$DER_HKDF_H]=oids_to_c.pm HKDF.asn1
+
#----- Conclusion
-$COMMON= $DER_RSA_COMMON $DER_DIGESTS_GEN $DER_WRAP_GEN
+$COMMON= $DER_RSA_COMMON $DER_DIGESTS_GEN $DER_WRAP_GEN $DER_HKDF_GEN
IF[{- !$disabled{dsa} -}]
$COMMON = $COMMON $DER_DSA_GEN $DER_DSA_AUX
--- /dev/null
+/*
+ * {- join("\n * ", @autowarntext) -}
+ *
+ * Copyright 2025 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 "prov/der_hkdf.h"
+
+/* Well known OIDs precompiled */
+{-
+ $OUT = oids_to_c::process_leaves('providers/common/der/HKDF.asn1',
+ { dir => $config{sourcedir},
+ filter => \&oids_to_c::filter_to_C });
+-}
--- /dev/null
+/*
+ * {- join("\n * ", @autowarntext) -}
+ *
+ * Copyright 2025 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 "internal/der.h"
+
+/* Well known OIDs precompiled */
+{-
+ $OUT = oids_to_c::process_leaves('providers/common/der/HKDF.asn1',
+ { dir => $config{sourcedir},
+ filter => \&oids_to_c::filter_to_H });
+-}
SOURCE[$TLS_ML_KEM_HYBRID_GOAL]=mlx_kmgmt.c
ENDIF
SOURCE[$ML_KEM_GOAL]=ml_kem_kmgmt.c
+ DEPEND[ml_kem_kmgmt.o]=../../common/include/prov/der_hkdf.h
ENDIF
SOURCE[$RSA_GOAL]=rsa_kmgmt.c
#include <openssl/rand.h>
#include <openssl/self_test.h>
#include <openssl/param_build.h>
+#include <openssl/cms.h>
#include "crypto/ml_kem.h"
#include "internal/fips.h"
#include "internal/param_build_set.h"
+#include "internal/sizes.h"
+#include "prov/der_hkdf.h"
+#include "prov/der_wrap.h"
#include "prov/implementations.h"
#include "prov/providercommon.h"
#include "prov/provider_ctx.h"
['PKEY_PARAM_PRIV_KEY', 'privkey', 'octet_string'],
['PKEY_PARAM_PUB_KEY', 'pubkey', 'octet_string'],
['PKEY_PARAM_ENCODED_PUBLIC_KEY', 'encpubkey', 'octet_string'],
- )); -}
+ ['PKEY_PARAM_CMS_RI_TYPE', 'ri_type', 'int'],
+ ['PKEY_PARAM_CMS_KEMRI_KDF_ALGORITHM', 'kemri_kdf_alg', 'octet_string'],
+ )); -}
static const OSSL_PARAM *ml_kem_gettable_params(void *provctx)
{
&ossl_ml_kem_encode_seed))
return 0;
}
+
+#ifndef OPENSSL_NO_CMS
+ if (p.ri_type != NULL && !OSSL_PARAM_set_int(p.ri_type, CMS_RECIPINFO_KEM))
+ return 0;
+
+ if (p.kemri_kdf_alg != NULL) {
+ uint8_t aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE];
+ int ret;
+ size_t aid_len = 0;
+ WPACKET pkt;
+ uint8_t *aid = NULL;
+
+ ret = WPACKET_init_der(&pkt, aid_buf, sizeof(aid_buf));
+ ret &= ossl_DER_w_begin_sequence(&pkt, -1)
+ && ossl_DER_w_precompiled(&pkt, -1, ossl_der_oid_id_alg_hkdf_with_sha256,
+ sizeof(ossl_der_oid_id_alg_hkdf_with_sha256))
+ && ossl_DER_w_end_sequence(&pkt, -1);
+ if (ret && WPACKET_finish(&pkt)) {
+ WPACKET_get_total_written(&pkt, &aid_len);
+ aid = WPACKET_get_curr(&pkt);
+ }
+ WPACKET_cleanup(&pkt);
+ if (!ret)
+ return 0;
+ if (aid != NULL && aid_len != 0 &&
+ !OSSL_PARAM_set_octet_string(p.kemri_kdf_alg, aid, aid_len))
+ return 0;
+ }
+#endif
+
return 1;
}
$no_rc2 = 1 if disabled("legacy");
-plan tests => 31;
+plan tests => 32;
ok(run(test(["pkcs7_test"])), "test pkcs7");
"verify both signature signatures with root");
is(compare($smcont, $out2), 0, "compare original message with verified message");
};
+
+subtest "ML-KEM KEMRecipientInfo tests for CMS" => sub {
+ plan tests => 5;
+
+ SKIP: {
+ skip "ML-KEM is not supported in this build", 5
+ if disabled("ml-kem") || $no_pqc;
+
+ ok(run(app(["openssl", "cms", @prov, "-encrypt", "-in", $smcont,
+ "-out", "mlkem512.cms",
+ "-recip", catfile($smdir, "sm_mlkem512.pem"),
+ "-aes-256-gcm", "-keyid" ])),
+ "CMS encrypt with ML-KEM-512 and default KDF");
+
+ ok(run(app(["openssl", "cms", @prov, "-decrypt", "-in", "mlkem512.cms",
+ "-out", "mlkem512.txt", "-recip", catfile($smdir, "sm_mlkem512.pem")]))
+ && compare_text($smcont, "mlkem512.txt") == 0,
+ "CMS decrypt with ML-KEM-512 and default KDF");
+
+ ok(run(app(["openssl", "cms", @prov, "-encrypt", "-in", $smcont,
+ "-out", "mlkem512_mlkem768.cms",
+ "-recip", catfile($smdir, "sm_mlkem512.pem"),
+ "-recip_kdf", "HKDF-SHA512",
+ "-recip", catfile($smdir, "sm_mlkem768.pem"),
+ "-recip_ukm", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+ "-aes-256-gcm", "-keyid" ])),
+ "CMS encrypt to multiple with ML-KEM and explicit KDF and UKM");
+
+ ok(run(app(["openssl", "cms", @prov, "-decrypt", "-in", "mlkem512_mlkem768.cms",
+ "-out", "mlkem512-2.txt",
+ "-recip", catfile($smdir, "sm_mlkem512.pem")]))
+ && compare_text($smcont, "mlkem512-2.txt") == 0,
+ "CMS decrypt with ML-KEM-512 and explicit KDF");
+
+ ok(run(app(["openssl", "cms", @prov, "-decrypt", "-in", "mlkem512_mlkem768.cms",
+ "-out", "mlkem768-2.txt",
+ "-recip", catfile($smdir, "sm_mlkem768.pem")]))
+ && compare_text($smcont, "mlkem768-2.txt") == 0,
+ "CMS decrypt with ML-KEM-768 and using UKM");
+ }
+};
basicConstraints = CA:FALSE
keyUsage = critical, keyAgreement
+[ kem_cert ]
+
+# These extensions are added when 'ca' signs a request for an end-entity
+# KEM certificate, for which only key encipherment makes sense
+
+basicConstraints = CA:FALSE
+keyUsage = critical, keyEncipherment
+
[ codesign_cert ]
# These extensions are added when 'ca' signs a request for a code-signing
$OPENSSL genpkey -algorithm SLH-DSA-SHAKE-256s -out sm_slhdsa_shake_256s.pem
gen sm_slhdsa_shake_256s.pem "/CN=Test SMIME EE SLH-DSA-SHAKE-256s" \
signer_cert >>sm_slhdsa_shake_256s.pem
+$OPENSSL genpkey -algorithm ML-KEM-512 -out sm_mlkem512.pem
+gen sm_mlkem512.pem "/CN=Test SMIME EE ML-KEM-512" kem_cert >>sm_mlkem512.pem
+$OPENSSL genpkey -algorithm ML-KEM-768 -out sm_mlkem768.pem
+gen sm_mlkem768.pem "/CN=Test SMIME EE ML-KEM-768" kem_cert >>sm_mlkem768.pem
--- /dev/null
+-----BEGIN PRIVATE KEY-----
+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
+SD8=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIFZDCCBEygAwIBAgIUDZwFC8YCwrUYC680GwJzZQV3LiQwDQYJKoZIhvcNAQEL
+BQAwRDELMAkGA1UEBhMCVUsxFjAUBgNVBAoMDU9wZW5TU0wgR3JvdXAxHTAbBgNV
+BAMMFFRlc3QgUy9NSU1FIFJTQSBSb290MCAXDTI1MDQwMTIzMzAyNVoYDzIxMjUw
+NDAxMjMzMDI1WjAjMSEwHwYDVQQDDBhUZXN0IFNNSU1FIEVFIE1MLUtFTS01MTIw
+ggMyMAsGCWCGSAFlAwQEAQOCAyEAHENcJVoLfDxG3tkf5oKt6NYeq3Wt2NMtrdSb
+Ffxuakq7F5dgqUg8Q0uM0JHHApA8zQOGhIKKZ9MGMdIj7qzBTqOQfnFY/VY7xbgq
+vuMLyiWDVfoqvJE/QtsPOlkLrXxayaFyXpGkIvN4c0bNytkqsvCwisBOoFV6KVR+
+mwFMeRky6JTDy/s/cuVhLHPEwQNzJKIV3DoXNxApkLLO+NM5gKJEa1ORGRmkCoCa
+dVOeuelZzriyxbquJnKz6Qx8fnUosOiB7euIjBoM3mXP0aitRrmeRyyYwZUnRCWp
+RZA2fXoEDpgw8ptYT7I38sEtq9DJDft/mSSJRxh9ISMZ63EZhwkdw6jGzrOPbKc5
+LdxZSqNaZVYAfmxjSfNPjcyVlRJeIYeiaWAtafebRaaxt+i3vilBRjZl3aBr6xR8
+xEi7LELHROAFAF04o+gqAcEKz/uZbwo4MMosycKGwvkG4vhozOVRRxZLRoxvtdl2
+zsugcuvMNsuq/AI++Gu4lhiXf4xXgNStBMB4IFapscQP7FOVjqg4RhwemgSrpqZ5
+r9ucagCni/Bj4usCVtUe96JwURlD7mREJwai6WO+TmZhZZpiI7GMeNZ7O3MNVPQv
+LQLKa+M13+AyL7Ror0a9nuc3snpNokwqU+pwf2ZDB8EfoxC4xoUrwixVYXHAtfMQ
+8lC9xnRCmcYoNkPKK3AUB8sGFLNpUmyEAKg976rA47tnoquOLMK5SkbL2OoRXnhg
+4glazUYxeYwla7J4gJcV7UqeiFjFFgDEvqUClFutD9oWymNKKLQUrKc8ocoYfIYT
+qHc7ywcn6zQoxEwZAuYiFJOLO/aR0kyW5TFJBfrNnePGvyF/zCdFcsSEDGMDjdJX
+DLUmabyRiMCrKyWiaoZvVhawf/yizHmR1kGvuZODaoqq1liZdMt23cpKb1sQ8Ckq
+GDwVnpsRs5UBX2wVKupRcOZq5FYZd1mVHNsxq4Al/JQSscS4Rderj1J7sUc2sfmh
+CXMyMQU3cQh1b1YSdEotFGRNjKsr9uLUo5dS+hdIb33XVS6/sLo3sgOw9P+V0XQY
+YvAdgTqjXTBbMAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgUgMB0GA1UdDgQWBBQV
+ZyiKX4cw/GQVdwh0k/RTwdkZijAfBgNVHSMEGDAWgBQVwRMha+JVX6dqHVcg1s/z
+qXNkWTANBgkqhkiG9w0BAQsFAAOCAQEAshcFKFVMj0P9tCgOVZ3G1CFXWCLJT67d
+sZaiHbL2PVpt+RKrqZNP2A9cbGGxp5BA+Uc00Nl6l0k1hV4GEk8S/3cgmyewwsWW
+cgZh0j0VvVcDllZqY3K/yb90nYMGWHuSQkmFTmexORzKpgWhnsKALuct01zlzlcN
+4QB4KtpWE31VqdICHBdgmeTfK89MHVpz0FJX0P7OOKckuvxfz46pE2x+EW0PQeiz
++VO7QlBFcR1KHQCWT8OimRPsj6h18O4Vz73V/aLepYXNdPttY2AuMkBInuCV4Uon
+Pkx+jAlrq1Avdimj2rgWl5nuEteCTr3dRapgt8hTMrlfN5jfmOs/fQ==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN PRIVATE KEY-----
+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
+be8=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+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
+-----END CERTIFICATE-----
-T CMAC_CTX
-T CMS_AuthenticatedData
-T CMS_CertificateChoices
+-T CMS_CMSORIforKEMOtherInfo
-T CMS_CompressedData
-T CMS_ContentInfo
-T CMS_DigestedData
-T CMS_IssuerAndSerialNumber
-T CMS_KEKIdentifier
-T CMS_KEKRecipientInfo
+-T CMS_KEMRecipientInfo
-T CMS_KeyAgreeRecipientIdentifier
-T CMS_KeyAgreeRecipientInfo
-T CMS_KeyTransRecipientInfo
OSSL_PARAM_set_octet_string_or_ptr ? 3_6_0 EXIST::FUNCTION:
OSSL_STORE_LOADER_settable_ctx_params ? 3_6_0 EXIST::FUNCTION:
X509_CRL_get0_tbs_sigalg ? 3_6_0 EXIST::FUNCTION:
+CMS_RecipientInfo_kemri_cert_cmp ? 3_6_0 EXIST::FUNCTION:CMS
+CMS_RecipientInfo_kemri_set0_pkey ? 3_6_0 EXIST::FUNCTION:CMS
+CMS_RecipientInfo_kemri_get0_ctx ? 3_6_0 EXIST::FUNCTION:CMS
+CMS_RecipientInfo_kemri_get0_kdf_alg ? 3_6_0 EXIST::FUNCTION:CMS
+CMS_RecipientInfo_kemri_set_ukm ? 3_6_0 EXIST::FUNCTION:CMS
CMS_RecipientInfo_get0_pkey_ctx(3)
CMS_RecipientInfo_kari_decrypt(3)
CMS_RecipientInfo_kari_get0_alg(3)
-CMS_RecipientInfo_kari_get0_ctx(3)
CMS_RecipientInfo_kari_get0_orig_id(3)
CMS_RecipientInfo_kari_get0_reks(3)
CMS_RecipientInfo_kari_orig_id_cmp(3)
'PKEY_PARAM_FIPS_KEY_CHECK' => "key-check",
'PKEY_PARAM_ALGORITHM_ID' => '*ALG_PARAM_ALGORITHM_ID',
'PKEY_PARAM_ALGORITHM_ID_PARAMS' => '*ALG_PARAM_ALGORITHM_ID_PARAMS',
+ 'PKEY_PARAM_CMS_RI_TYPE' => "ri-type", # integer
+ 'PKEY_PARAM_CMS_KEMRI_KDF_ALGORITHM' => "kemri-kdf-alg",
# Diffie-Hellman/DSA Parameters
'PKEY_PARAM_FFC_P' => "p",