]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Implement KEMRecipientInfo (RFC9629) in CMS
authorDaniel Van Geest <daniel.vangeest@cryptonext-security.com>
Thu, 3 Apr 2025 10:19:43 +0000 (11:19 +0100)
committerTomas Mraz <tomas@openssl.org>
Wed, 30 Jul 2025 09:39:04 +0000 (11:39 +0200)
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)

41 files changed:
.gitignore
CHANGES.md
apps/cms.c
crypto/cms/build.info
crypto/cms/cms_asn1.c
crypto/cms/cms_env.c
crypto/cms/cms_err.c
crypto/cms/cms_kari.c
crypto/cms/cms_kem.c [new file with mode: 0644]
crypto/cms/cms_kemri.c [new file with mode: 0644]
crypto/cms/cms_local.h
crypto/cms/cms_pwri.c
crypto/cms/cms_smime.c
crypto/err/openssl.txt
crypto/objects/obj_dat.h
crypto/objects/obj_mac.num
crypto/objects/objects.txt
doc/man1/openssl-cms.pod.in
doc/man3/CMS_add1_recipient_cert.pod
doc/man3/CMS_encrypt.pod
doc/man3/CMS_get0_RecipientInfos.pod
doc/man7/provider-keymgmt.pod
fuzz/oids.txt
include/openssl/cms.h.in
include/openssl/cmserr.h
include/openssl/obj_mac.h
providers/common/der/HKDF.asn1 [new file with mode: 0644]
providers/common/der/build.info
providers/common/der/der_hkdf_gen.c.in [new file with mode: 0644]
providers/common/include/prov/der_hkdf.h.in [new file with mode: 0644]
providers/implementations/keymgmt/build.info
providers/implementations/keymgmt/ml_kem_kmgmt.c.in
test/recipes/80-test_cms.t
test/smime-certs/ca.cnf
test/smime-certs/mksmime-certs.sh
test/smime-certs/sm_mlkem512.pem [new file with mode: 0644]
test/smime-certs/sm_mlkem768.pem [new file with mode: 0644]
util/indent.pro
util/libcrypto.num
util/missingcrypto.txt
util/perl/OpenSSL/paramnames.pm

index 7ab32ad61d32cdf2c5f7b7dae32228d70a6e5548..76f9b988a902db2cc89c542ae94675a4db225fd3 100644 (file)
@@ -75,6 +75,7 @@ providers/common/der/der_rsa_gen.c
 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
@@ -84,6 +85,7 @@ providers/common/include/prov/der_digests.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
index 2c0b9300e40a9cf0da50c16893c7d1eb85818379..d3437a5e4dea5924ef5249c90ed7252a6fbd6282 100644 (file)
@@ -145,6 +145,11 @@ OpenSSL 3.6
 
    *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
 -----------
 
index 0a2ce7f571f7f949ae99bc4ace72d5f2c9245481..7e2d6ba32a59e5b180b182f625629c1e1e405e1c 100644 (file)
@@ -53,6 +53,7 @@ static int cms_set_pkey_param(EVP_PKEY_CTX *pctx,
 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;
@@ -60,6 +61,14 @@ struct cms_key_param_st {
     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,
@@ -85,7 +94,8 @@ typedef enum OPTION_choice {
     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[] = {
@@ -167,13 +177,15 @@ 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"},
@@ -281,6 +293,19 @@ static CMS_ContentInfo *load_content_info(int informat, BIO *in, int flags,
     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;
@@ -319,6 +344,8 @@ int cms_main(int argc, char **argv)
     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;
@@ -653,6 +680,46 @@ int cms_main(int argc, char **argv)
                 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;
@@ -831,6 +898,9 @@ int cms_main(int argc, char **argv)
     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))
@@ -990,7 +1060,9 @@ int cms_main(int argc, char **argv)
             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;
@@ -998,14 +1070,19 @@ int cms_main(int argc, char **argv)
             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))
@@ -1018,12 +1095,39 @@ int cms_main(int argc, char **argv)
             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;
+                }
             }
         }
 
@@ -1315,6 +1419,14 @@ int cms_main(int argc, char **argv)
         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);
index 5fabea7c53d1d9b8008aa2c7125f4c3fcb76ee3e..9ff76c8d852f6c38bab551633321d744407a6808 100644 (file)
@@ -2,4 +2,4 @@ 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_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
index 1ed2399617755659fac3dd12c0cd250a4e0ee3cc..e2f0040dfb0923ff6981cc27b8dac68b2dad65b7 100644 (file)
@@ -201,10 +201,52 @@ ASN1_SEQUENCE(CMS_PasswordRecipientInfo) = {
         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,
@@ -224,6 +266,23 @@ 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;
 }
@@ -234,7 +293,7 @@ ASN1_CHOICE_cb(CMS_RecipientInfo, cms_ri_cb) = {
         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),
@@ -430,3 +489,33 @@ int CMS_SharedInfo_encode(unsigned char **pder, X509_ALGOR *kekalg,
     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));
+}
index 5c0872069379ecc9c03f2c980951b7b6c1f27301..47432cee144f2a9ed1fc8d67041ce34ce55516fb 100644 (file)
@@ -7,6 +7,12 @@
  * 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>
@@ -14,6 +20,7 @@
 #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"
@@ -111,9 +118,12 @@ int ossl_cms_env_asn1_ctrl(CMS_RecipientInfo *ri, int cmd)
 {
     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)
@@ -121,8 +131,13 @@ int ossl_cms_env_asn1_ctrl(CMS_RecipientInfo *ri, int cmd)
         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);
@@ -202,6 +217,9 @@ void ossl_cms_RecipientInfos_set_cmsctx(CMS_ContentInfo *cms)
             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;
             }
@@ -220,6 +238,8 @@ EVP_PKEY_CTX *CMS_RecipientInfo_get0_pkey_ctx(CMS_RecipientInfo *ri)
         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;
 }
 
@@ -336,7 +356,7 @@ static int cms_RecipientInfo_ktri_init(CMS_RecipientInfo *ri, X509 *recip,
     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;
@@ -423,6 +443,11 @@ CMS_RecipientInfo *CMS_add1_recipient(CMS_ContentInfo *cms, X509 *recip,
             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;
@@ -750,7 +775,7 @@ CMS_RecipientInfo *CMS_add0_recipient_key(CMS_ContentInfo *cms, int nid,
         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;
 
@@ -1025,6 +1050,9 @@ int CMS_RecipientInfo_decrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri)
     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;
@@ -1046,6 +1074,9 @@ int CMS_RecipientInfo_encrypt(const CMS_ContentInfo *cms, CMS_RecipientInfo *ri)
     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;
@@ -1100,7 +1131,8 @@ static void cms_env_set_version(CMS_EnvelopedData *env)
 
     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
@@ -1337,6 +1369,18 @@ err:
  */
 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;
@@ -1350,7 +1394,7 @@ int ossl_cms_pkey_get_ri_type(EVP_PKEY *pk)
         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) {
@@ -1359,7 +1403,25 @@ int ossl_cms_pkey_get_ri_type(EVP_PKEY *pk)
         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)
@@ -1381,3 +1443,79 @@ 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;
+}
index 98d6bea5f028a3055220f31f8f018fb27ab176e0..1815c51db1c1ecc48861cb3318036723dab920d1 100644 (file)
@@ -88,6 +88,7 @@ static const ERR_STRING_DATA CMS_str_reasons[] = {
      "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"},
@@ -140,6 +141,8 @@ static const ERR_STRING_DATA CMS_str_reasons[] = {
     {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),
@@ -148,6 +151,8 @@ static const ERR_STRING_DATA CMS_str_reasons[] = {
      "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),
index 8f799dc1804c72bb8b66b9f2f0e2b698104bb8a4..4d476757bc24f30fe82f660ea47dd68b97d8aa0e 100644 (file)
@@ -7,12 +7,6 @@
  * 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>
@@ -349,7 +343,7 @@ int ossl_cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri,  X509 *recip,
     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;
@@ -412,67 +406,6 @@ int ossl_cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri,  X509 *recip,
     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,
@@ -492,7 +425,7 @@ 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
diff --git a/crypto/cms/cms_kem.c b/crypto/cms/cms_kem.c
new file mode 100644 (file)
index 0000000..ac69809
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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(&params[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;
+}
diff --git a/crypto/cms/cms_kemri.c b/crypto/cms/cms_kemri.c
new file mode 100644 (file)
index 0000000..2af94b7
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * 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;
+}
index 8ed67f5c19f47f4f6ebd95dffaefb826eaad3c15..990f86d221bb7c44e3370ad1150c47b5fcb92d64 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
@@ -41,6 +41,7 @@ typedef struct CMS_KEKIdentifier_st CMS_KEKIdentifier;
 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;
 
@@ -142,7 +143,11 @@ struct CMS_EncryptedContentInfo_st {
 };
 
 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;
@@ -150,6 +155,8 @@ struct CMS_RecipientInfo_st {
         CMS_PasswordRecipientInfo *pwri;
         CMS_OtherRecipientInfo *ori;
     } d;
+    /* internal type, including ORI types */
+    int type;
 };
 
 typedef CMS_SignerIdentifier CMS_RecipientIdentifier;
@@ -245,7 +252,29 @@ struct CMS_PasswordRecipientInfo_st {
 
 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 {
@@ -460,6 +489,7 @@ int ossl_cms_pkey_get_ri_type(EVP_PKEY *pk);
 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,
@@ -470,6 +500,20 @@ 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);
@@ -486,6 +530,7 @@ int ossl_cms_dh_envelope(CMS_RecipientInfo *ri, int decrypt);
 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);
@@ -496,10 +541,12 @@ DECLARE_ASN1_ITEM(CMS_EncryptedData)
 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)
index 5f68ad4b36c03c1aa655f5d5bceadf77996de45f..106bd98dc7fd9eb40888732f29ef25225ae5b884 100644 (file)
@@ -138,7 +138,7 @@ CMS_RecipientInfo *CMS_add0_recipient_password(CMS_ContentInfo *cms,
         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;
index 3f3d93fa0095805dc8184a55fc844b6e2f1c972d..f9927fcf466cf2e12e07924657cbc22fdebe75e8 100644 (file)
@@ -750,6 +750,14 @@ int CMS_decrypt_set1_pkey_and_peer(CMS_ContentInfo *cms, EVP_PKEY *pk,
                 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)) {
index 355b20d627dbfcaa05368b920bdddaecc18da451..e4e75c3b15cb5abda7d1902eee7399ce633a4ee2 100644 (file)
@@ -354,6 +354,7 @@ CMS_R_NEED_ONE_SIGNER:164:need one signer
 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
@@ -394,11 +395,13 @@ CMS_R_UNABLE_TO_FINALIZE_CONTEXT:147:unable to finalize context
 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
index 5b6a97c6fbb06f22eac4fff8350235ed11843578..40d6f9ca4d14dcc3318af60c91bbef2320563acb 100644 (file)
@@ -10,7 +10,7 @@
  */
 
 /* 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 */
@@ -1351,9 +1351,11 @@ static const unsigned char so[9550] = {
     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]},
@@ -2854,9 +2856,11 @@ static const ASN1_OBJECT nid_objs[NUM_NID] = {
     {"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" */
@@ -3744,6 +3748,8 @@ static const unsigned int sn_objs[NUM_SN] = {
      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" */
@@ -4350,7 +4356,7 @@ static const unsigned int sn_objs[NUM_SN] = {
     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" */
@@ -5367,6 +5373,8 @@ static const unsigned int ln_objs[NUM_LN] = {
      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" */
@@ -5844,7 +5852,7 @@ static const unsigned int ln_objs[NUM_LN] = {
      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 */
@@ -7051,6 +7059,7 @@ static const unsigned int obj_objs[NUM_OBJ] = {
      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 */
@@ -7169,6 +7178,7 @@ static const unsigned int obj_objs[NUM_OBJ] = {
      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 */
index 842b024dcc0284f89b0c20fdb428b2d9b1314258..b48d054d3295f5401774519d8bcd95b99d00c63c 100644 (file)
@@ -1496,3 +1496,5 @@ aes_256_cbc_hmac_sha512_etm               1495
 HKDF_SHA256            1496
 HKDF_SHA384            1497
 HKDF_SHA512            1498
+id_smime_ori           1499
+id_smime_ori_kem               1500
index bbd757b8cf1bca150023610fab1e37795456e6f3..feed79b6738f0593d152a24bc34c080e2abbfa17 100644 (file)
@@ -251,6 +251,7 @@ SMIME 3                     : id-smime-alg
 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
@@ -355,6 +356,9 @@ id-smime-cti 4              : id-smime-cti-ets-proofOfSender
 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
index 30e7734d0f5839a16f2f6ad33a6a082a7ffcfee0..25e61bc719a207448e70e0a546dc4f02ef86f5d8 100644 (file)
@@ -75,6 +75,8 @@ Encryption options:
 [B<-aes256-wrap>]
 [B<-des3-wrap>]
 [B<-debug_decrypt>]
+[B<-recip_kdf> I<kdf>]
+[B<-recip_ukm> I<ukm>]
 
 Signing options:
 
@@ -450,6 +452,19 @@ Depending on the OpenSSL build options used, B<-des3-wrap> may not be supported.
 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
@@ -937,6 +952,8 @@ The B<-engine> option was deprecated in OpenSSL 3.0.
 
 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.
index 0855d5321b060e200a5fc0a7c13c09140ccad7c5..9810e910bed7013d9074a62dc2f8daa4c56966ac 100644 (file)
@@ -30,7 +30,8 @@ The originator-related fields are relevant only in case when the keyAgreement
 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
index cddd89447b473add9463b4981c81869f578b60b9..bccf7fe432dd5f16bf96bb496d15a868de13e319 100644 (file)
@@ -78,10 +78,12 @@ BIO_new_CMS().
 
 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>.
@@ -104,7 +106,7 @@ The B<CMS_STREAM> flag was first supported in OpenSSL 1.0.0.
 
 =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
index eb755f52431068fa58a126ff900916b1b93564b4..ef412066ce3e7127358742ed1778f25767a8771b 100644 (file)
@@ -6,9 +6,12 @@ CMS_get0_RecipientInfos, CMS_RecipientInfo_type,
 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
@@ -27,6 +30,7 @@ CMS_RecipientInfo_decrypt, CMS_RecipientInfo_encrypt
  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,
@@ -36,6 +40,13 @@ CMS_RecipientInfo_decrypt, CMS_RecipientInfo_encrypt
                                     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);
@@ -47,7 +58,8 @@ structures associated with a CMS EnvelopedData structure.
 
 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
@@ -69,6 +81,10 @@ must be of type CMS_RECIPINFO_AGREE.
 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
@@ -88,6 +104,27 @@ CMS_RecipientInfo_set0_key() associates the symmetric key B<key> of length
 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.
@@ -125,12 +162,20 @@ CMS_get0_RecipientInfos() returns all CMS_RecipientInfo structures, or NULL if
 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)>.
 
@@ -143,9 +188,13 @@ L<ERR_get_error(3)>, L<CMS_decrypt(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
index 438309c2f0859ea7a01b3bba43d8577afa7255a8..237ea87a7fecbceb45b757dafbd4dbe8ec6a4619 100644 (file)
@@ -442,6 +442,30 @@ its argument I<mdname>.  This signifies that no digest has to be specified
 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:
index 29a801ae37bef0ab18435786a2dbf53a8e1e83f2..b299cc2d64f36df0b6deefb8a73dce496a9b0b4a 100644 (file)
@@ -1347,3 +1347,5 @@ OBJ_SLH_DSA_SHAKE_256f_WITH_SHAKE256="\x60\x86\x48\x01\x65\x03\x04\x03\x2E"
 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"
index 0ae75abdfe6fe9b92f7a90177ed84a7c09100f2d..c47b2ca845846519262bc2a591739182c7f4369f 100644 (file)
@@ -70,6 +70,7 @@ CMS_ContentInfo *CMS_ContentInfo_new_ex(OSSL_LIB_CTX *libctx, const char *propq)
 # define CMS_RECIPINFO_KEK               2
 # define CMS_RECIPINFO_PASS              3
 # define CMS_RECIPINFO_OTHER             4
+# define CMS_RECIPINFO_KEM               5
 
 /* S/MIME related flags */
 
@@ -401,6 +402,14 @@ int CMS_RecipientInfo_kari_decrypt(CMS_ContentInfo *cms,
 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 \
index 5cfe07dbb3a344dc49c1c1ae6a564572fc2b15be..606cc114b86edb32ea4b341f9eb0c662a7ab08c9 100644 (file)
@@ -67,6 +67,7 @@
 #  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
index a2f4bc01605e3a6876498efc3482d44ff6138188..440ec10df675e6e022d36f8a674789666ec8a015 100644 (file)
 #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
diff --git a/providers/common/der/HKDF.asn1 b/providers/common/der/HKDF.asn1
new file mode 100644 (file)
index 0000000..40145f9
--- /dev/null
@@ -0,0 +1,16 @@
+-- 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 }
index cc881be85bd2ce06890dfe4cd42e4d1ba8de1ea7..a24a8c3635fc0743c4eba6db6cf3bcc98f5cded8 100644 (file)
@@ -127,9 +127,20 @@ IF[{- !$disabled{'slh-dsa'} -}]
   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
diff --git a/providers/common/der/der_hkdf_gen.c.in b/providers/common/der/der_hkdf_gen.c.in
new file mode 100644 (file)
index 0000000..43abbd1
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * {- 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 });
+-}
diff --git a/providers/common/include/prov/der_hkdf.h.in b/providers/common/include/prov/der_hkdf.h.in
new file mode 100644 (file)
index 0000000..b0820ed
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * {- 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 });
+-}
index f5ae1ece8869cff202481adec961bed665b5f4fd..452212535035bb238f57028eb0248892c815d141 100644 (file)
@@ -47,6 +47,7 @@ IF[{- !$disabled{'ml-kem'} -}]
     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
index 9bab2f3b43865707a2adefc0d1573ae43b57c7cc..8d68128406d4dee3d143d1bdd3e92220ff10ee6f 100644 (file)
@@ -19,9 +19,13 @@ use OpenSSL::paramnames qw(produce_param_decoder);
 #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"
@@ -494,7 +498,9 @@ static int ml_kem_import(void *vkey, int selection, const OSSL_PARAM params[])
                           ['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)
 {
@@ -612,6 +618,36 @@ static int ml_kem_get_params(void *vkey, OSSL_PARAM params[])
                                   &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;
 }
 
index 3feea1e48e15ed2f7904edb6b413826b3b9a0de5..ea19f3b2eac95a61cfcbe5117f93ac6642b06a9e 100644 (file)
@@ -53,7 +53,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib)
 
 $no_rc2 = 1 if disabled("legacy");
 
-plan tests => 31;
+plan tests => 32;
 
 ok(run(test(["pkcs7_test"])), "test pkcs7");
 
@@ -1528,3 +1528,44 @@ subtest "sign and verify with multiple keys" => sub {
        "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");
+    }
+};
index 1bdffadf672dadc0d4b3b915c89df2c1d9d7224b..2cbf735bd243bf41d154acc8dde7714423d6c3dd 100644 (file)
@@ -58,6 +58,14 @@ keyUsage = critical, digitalSignature
 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
index d989683faae09eacabae8b11695026cbbbad2d9c..172e82cd5aa3f0a99db7eeb6c63205001cfe5af2 100755 (executable)
@@ -80,3 +80,7 @@ gen sm_slhdsa_shake_128s.pem "/CN=Test SMIME EE SLH-DSA-SHAKE-128s" \
 $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
diff --git a/test/smime-certs/sm_mlkem512.pem b/test/smime-certs/sm_mlkem512.pem
new file mode 100644 (file)
index 0000000..5e7b8bf
--- /dev/null
@@ -0,0 +1,70 @@
+-----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-----
diff --git a/test/smime-certs/sm_mlkem768.pem b/test/smime-certs/sm_mlkem768.pem
new file mode 100644 (file)
index 0000000..be71ad0
--- /dev/null
@@ -0,0 +1,94 @@
+-----BEGIN PRIVATE KEY-----
+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
+be8=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+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
+-----END CERTIFICATE-----
index bc626e4a4bcbacef026e8a69ec46139ab8472c6f..1937cfa476fe4152bf0b9761b1f2d0b67f00ea9e 100644 (file)
 -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
index 2228aa3bf60416f42fd54c4a01103d8f310d0d24..d1667ee2a67429f00df6aa519925f3b265fc4aea 100644 (file)
@@ -5931,3 +5931,8 @@ i2d_PKCS8PrivateKey                     ? 3_6_0   EXIST::FUNCTION:
 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
index 9191854ae77ade1a1bd4ee41b9117d40c59ba8f9..ed586f8e30e41a878449713ec3893499876a816a 100644 (file)
@@ -290,7 +290,6 @@ CMS_RecipientEncryptedKey_get0_id(3)
 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)
index bcb27cf4f371e5376aed5eb278ae870de021781d..203c6f73b31063229a1a601cceca63237d903bcf 100644 (file)
@@ -315,6 +315,8 @@ my %params = (
     '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",