From: Daniel Van Geest Date: Thu, 3 Apr 2025 10:19:43 +0000 (+0100) Subject: Implement KEMRecipientInfo (RFC9629) in CMS X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d0899abb1b7654922b2272070d8fb593d8b13cff;p=thirdparty%2Fopenssl.git Implement KEMRecipientInfo (RFC9629) in CMS 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 Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/27681) --- diff --git a/.gitignore b/.gitignore index 7ab32ad61d3..76f9b988a90 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/CHANGES.md b/CHANGES.md index 2c0b9300e40..d3437a5e4de 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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 ----------- diff --git a/apps/cms.c b/apps/cms.c index 0a2ce7f571f..7e2d6ba32a5 100644 --- a/apps/cms.c +++ b/apps/cms.c @@ -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); diff --git a/crypto/cms/build.info b/crypto/cms/build.info index 5fabea7c53d..9ff76c8d852 100644 --- a/crypto/cms/build.info +++ b/crypto/cms/build.info @@ -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 diff --git a/crypto/cms/cms_asn1.c b/crypto/cms/cms_asn1.c index 1ed23996177..e2f0040dfb0 100644 --- a/crypto/cms/cms_asn1.c +++ b/crypto/cms/cms_asn1.c @@ -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)); +} diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c index 5c087206937..47432cee144 100644 --- a/crypto/cms/cms_env.c +++ b/crypto/cms/cms_env.c @@ -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 #include @@ -14,6 +20,7 @@ #include #include #include +#include #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; +} diff --git a/crypto/cms/cms_err.c b/crypto/cms/cms_err.c index 98d6bea5f02..1815c51db1c 100644 --- a/crypto/cms/cms_err.c +++ b/crypto/cms/cms_err.c @@ -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), diff --git a/crypto/cms/cms_kari.c b/crypto/cms/cms_kari.c index 8f799dc1804..4d476757bc2 100644 --- a/crypto/cms/cms_kari.c +++ b/crypto/cms/cms_kari.c @@ -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 #include @@ -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 index 00000000000..ac698099e14 --- /dev/null +++ b/crypto/cms/cms_kem.c @@ -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 +#include +#include +#include +#include +#include +#include "internal/sizes.h" +#include "crypto/asn1.h" +#include "crypto/evp.h" +#include "cms_local.h" + +static int kem_cms_decrypt(CMS_RecipientInfo *ri) +{ + uint32_t *kekLength; + X509_ALGOR *wrap; + EVP_PKEY_CTX *pctx; + EVP_CIPHER_CTX *kekctx; + uint32_t cipher_length; + char name[OSSL_MAX_NAME_SIZE]; + EVP_CIPHER *kekcipher = NULL; + int rv = 0; + + if (!ossl_cms_RecipientInfo_kemri_get0_alg(ri, &kekLength, &wrap)) + goto err; + + pctx = CMS_RecipientInfo_get0_pkey_ctx(ri); + if (pctx == NULL) + goto err; + + kekctx = CMS_RecipientInfo_kemri_get0_ctx(ri); + if (kekctx == NULL) + goto err; + + OBJ_obj2txt(name, sizeof(name), wrap->algorithm, 0); + kekcipher = EVP_CIPHER_fetch(pctx->libctx, name, pctx->propquery); + if (kekcipher == NULL || EVP_CIPHER_get_mode(kekcipher) != EVP_CIPH_WRAP_MODE) + goto err; + if (!EVP_EncryptInit_ex(kekctx, kekcipher, NULL, NULL, NULL)) + goto err; + if (EVP_CIPHER_asn1_to_param(kekctx, wrap->parameter) <= 0) + goto err; + + cipher_length = EVP_CIPHER_CTX_get_key_length(kekctx); + if (cipher_length != *kekLength) { + ERR_raise(ERR_LIB_CMS, CMS_R_INVALID_KEY_LENGTH); + goto err; + } + + rv = 1; +err: + EVP_CIPHER_free(kekcipher); + return rv; +} + +static int kem_cms_encrypt(CMS_RecipientInfo *ri) +{ + uint32_t *kekLength; + X509_ALGOR *wrap; + X509_ALGOR *kdf; + EVP_PKEY_CTX *pctx; + EVP_PKEY *pkey; + int security_bits; + const ASN1_OBJECT *kdf_obj = NULL; + unsigned char kemri_x509_algor[OSSL_MAX_ALGORITHM_ID_SIZE]; + OSSL_PARAM params[2]; + X509_ALGOR *x509_algor = NULL; + EVP_CIPHER_CTX *kekctx; + int wrap_nid; + int rv = 0; + + if (!ossl_cms_RecipientInfo_kemri_get0_alg(ri, &kekLength, &wrap)) + goto err; + + kdf = CMS_RecipientInfo_kemri_get0_kdf_alg(ri); + if (kdf == NULL) + goto err; + + pctx = CMS_RecipientInfo_get0_pkey_ctx(ri); + if (pctx == NULL) + goto err; + + pkey = EVP_PKEY_CTX_get0_pkey(pctx); + if (pkey == NULL) + goto err; + + security_bits = EVP_PKEY_get_security_bits(pkey); + if (security_bits == 0) + goto err; + + X509_ALGOR_get0(&kdf_obj, NULL, NULL, kdf); + if (kdf_obj == NULL || OBJ_obj2nid(kdf_obj) == NID_undef) { + /* + * If the KDF OID hasn't already been set, then query the provider + * for a default KDF. + */ + params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_CMS_KEMRI_KDF_ALGORITHM, + kemri_x509_algor, sizeof(kemri_x509_algor)); + params[1] = OSSL_PARAM_construct_end(); + if (!EVP_PKEY_get_params(pkey, params)) + goto err; + if (OSSL_PARAM_modified(¶ms[0])) { + const unsigned char *p = kemri_x509_algor; + + x509_algor = d2i_X509_ALGOR(NULL, &p, (long)params[0].return_size); + if (x509_algor == NULL) + goto err; + if (!X509_ALGOR_copy(kdf, x509_algor)) + goto err; + } else { + if (!X509_ALGOR_set0(kdf, OBJ_nid2obj(NID_HKDF_SHA256), V_ASN1_UNDEF, NULL)) + return 0; + } + } + + /* Get wrap NID */ + kekctx = CMS_RecipientInfo_kemri_get0_ctx(ri); + if (kekctx == NULL) + goto err; + *kekLength = EVP_CIPHER_CTX_get_key_length(kekctx); + wrap_nid = EVP_CIPHER_CTX_get_type(kekctx); + + /* Package wrap algorithm in an AlgorithmIdentifier */ + ASN1_OBJECT_free(wrap->algorithm); + ASN1_TYPE_free(wrap->parameter); + wrap->algorithm = OBJ_nid2obj(wrap_nid); + wrap->parameter = ASN1_TYPE_new(); + if (wrap->parameter == NULL) + goto err; + if (EVP_CIPHER_param_to_asn1(kekctx, wrap->parameter) <= 0) + goto err; + if (ASN1_TYPE_get(wrap->parameter) == NID_undef) { + ASN1_TYPE_free(wrap->parameter); + wrap->parameter = NULL; + } + + rv = 1; +err: + X509_ALGOR_free(x509_algor); + return rv; +} + +int ossl_cms_kem_envelope(CMS_RecipientInfo *ri, int decrypt) +{ + assert(decrypt == 0 || decrypt == 1); + + if (decrypt == 1) + return kem_cms_decrypt(ri); + + if (decrypt == 0) + return kem_cms_encrypt(ri); + + ERR_raise(ERR_LIB_CMS, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE); + return 0; +} diff --git a/crypto/cms/cms_kemri.c b/crypto/cms/cms_kemri.c new file mode 100644 index 00000000000..2af94b7c01c --- /dev/null +++ b/crypto/cms/cms_kemri.c @@ -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 +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/crypto/cms/cms_local.h b/crypto/cms/cms_local.h index 8ed67f5c19f..990f86d221b 100644 --- a/crypto/cms/cms_local.h +++ b/crypto/cms/cms_local.h @@ -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) diff --git a/crypto/cms/cms_pwri.c b/crypto/cms/cms_pwri.c index 5f68ad4b36c..106bd98dc7f 100644 --- a/crypto/cms/cms_pwri.c +++ b/crypto/cms/cms_pwri.c @@ -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; diff --git a/crypto/cms/cms_smime.c b/crypto/cms/cms_smime.c index 3f3d93fa009..f9927fcf466 100644 --- a/crypto/cms/cms_smime.c +++ b/crypto/cms/cms_smime.c @@ -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)) { diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 355b20d627d..e4e75c3b15c 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -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 diff --git a/crypto/objects/obj_dat.h b/crypto/objects/obj_dat.h index 5b6a97c6fbb..40d6f9ca4d1 100644 --- a/crypto/objects/obj_dat.h +++ b/crypto/objects/obj_dat.h @@ -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 */ diff --git a/crypto/objects/obj_mac.num b/crypto/objects/obj_mac.num index 842b024dcc0..b48d054d329 100644 --- a/crypto/objects/obj_mac.num +++ b/crypto/objects/obj_mac.num @@ -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 diff --git a/crypto/objects/objects.txt b/crypto/objects/objects.txt index bbd757b8cf1..feed79b6738 100644 --- a/crypto/objects/objects.txt +++ b/crypto/objects/objects.txt @@ -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 diff --git a/doc/man1/openssl-cms.pod.in b/doc/man1/openssl-cms.pod.in index 30e7734d0f5..25e61bc719a 100644 --- a/doc/man1/openssl-cms.pod.in +++ b/doc/man1/openssl-cms.pod.in @@ -75,6 +75,8 @@ Encryption options: [B<-aes256-wrap>] [B<-des3-wrap>] [B<-debug_decrypt>] +[B<-recip_kdf> I] +[B<-recip_ukm> I] 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 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 type. Any KDF that takes B and +B parameters and is otherwise fully defined by its name or +OID can be used, for example B. + +=item B<-recip_ukm> + +This option sets the B type's optional user keying material (UKM) +in hexadecimal form. The UKM will be encoded, along with other information, into the +B parameter of the B 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. diff --git a/doc/man3/CMS_add1_recipient_cert.pod b/doc/man3/CMS_add1_recipient_cert.pod index 0855d5321b0..9810e910bed 100644 --- a/doc/man3/CMS_add1_recipient_cert.pod +++ b/doc/man3/CMS_add1_recipient_cert.pod @@ -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 to CMS_ContentInfo enveloped -data structure B as a KeyTransRecipientInfo structure. +data structure B as a KeyTransRecipientInfo or KEMRecipientInfo structure, +or as a KeyAgreeRecipientInfo structure with an ephemeral key. CMS_add0_recipient_key() adds symmetric key B of length B using wrapping algorithm B, identifier B of length B and optional diff --git a/doc/man3/CMS_encrypt.pod b/doc/man3/CMS_encrypt.pod index cddd89447b4..bccf7fe432d 100644 --- a/doc/man3/CMS_encrypt.pod +++ b/doc/man3/CMS_encrypt.pod @@ -78,10 +78,12 @@ BIO_new_CMS(). The recipients specified in B use a CMS KeyTransRecipientInfo info structure. KEKRecipientInfo is also supported using the flag B -and CMS_add0_recipient_key(). +and CMS_add0_recipient_key(). KEMRecipientInfo is also supported using the +flag B and CMS_add1_recipient(). The parameter B may be NULL if B 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 and the property query I. @@ -104,7 +106,7 @@ The B 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 diff --git a/doc/man3/CMS_get0_RecipientInfos.pod b/doc/man3/CMS_get0_RecipientInfos.pod index eb755f52431..ef412066ce3 100644 --- a/doc/man3/CMS_get0_RecipientInfos.pod +++ b/doc/man3/CMS_get0_RecipientInfos.pod @@ -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. 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, which @@ -69,6 +81,10 @@ must be of type CMS_RECIPINFO_AGREE. CMS_RecipientInfo_kari_set0_pkey() associates the private key B with the CMS_RecipientInfo structure B, 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 must be of type CMS_RECIPINFO_AGREE. + CMS_RecipientInfo_kekri_get0_id() retrieves the key information from the CMS_RecipientInfo structure B 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 of length B with the CMS_RecipientInfo structure B, which must be of type CMS_RECIPINFO_KEK. +CMS_RecipientInfo_kemri_cert_cmp() compares the certificate B against the +CMS_RecipientInfo structure B, 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 with the +CMS_RecipientInfo structure B, 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 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 must be of type CMS_RECIPINFO_KEM. If the +caller doesn't specify a KDF algorithm, B 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 parameter of the RecipientInfo's KDF. The +CMS_RecipientInfo structure B must be of type CMS_RECIPINFO_KEM. + CMS_RecipientInfo_decrypt() attempts to decrypt CMS_RecipientInfo structure B in structure B. 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. @@ -143,9 +188,13 @@ L, L B and B were added in OpenSSL 3.0. +B, B, +B, B +and B 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 diff --git a/doc/man7/provider-keymgmt.pod b/doc/man7/provider-keymgmt.pod index 438309c2f08..237ea87a7fe 100644 --- a/doc/man7/provider-keymgmt.pod +++ b/doc/man7/provider-keymgmt.pod @@ -442,6 +442,30 @@ its argument I. 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) + +The value should be the CMS RecipientInfo type for the given key, for example +B or B. +The value that can be given through this parameter is found in +F<< >>, with the macros having names starting with +C. + +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. + +=item "kemri-kdf-alg" (B) + +The value should be the DER-encoded X509_ALGOR for the default KDF for this key +if it supports the KEMRecipientInfo (B) type. + +Any KDF that takes B and B parameters +and is otherwise fully defined by its OID can be used, for example B. + +If B is not implemented, B +will be used as the default KDF. + =back The OpenSSL FIPS provider also supports the following parameters: diff --git a/fuzz/oids.txt b/fuzz/oids.txt index 29a801ae37b..b299cc2d64f 100644 --- a/fuzz/oids.txt +++ b/fuzz/oids.txt @@ -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" diff --git a/include/openssl/cms.h.in b/include/openssl/cms.h.in index 0ae75abdfe6..c47b2ca8458 100644 --- a/include/openssl/cms.h.in +++ b/include/openssl/cms.h.in @@ -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 \ diff --git a/include/openssl/cmserr.h b/include/openssl/cmserr.h index 5cfe07dbb3a..606cc114b86 100644 --- a/include/openssl/cmserr.h +++ b/include/openssl/cmserr.h @@ -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 @@ -106,10 +107,12 @@ # 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 diff --git a/include/openssl/obj_mac.h b/include/openssl/obj_mac.h index a2f4bc01605..440ec10df67 100644 --- a/include/openssl/obj_mac.h +++ b/include/openssl/obj_mac.h @@ -778,6 +778,10 @@ #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 @@ -1113,6 +1117,10 @@ #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 index 00000000000..40145f9abee --- /dev/null +++ b/providers/common/der/HKDF.asn1 @@ -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 } diff --git a/providers/common/der/build.info b/providers/common/der/build.info index cc881be85bd..a24a8c3635f 100644 --- a/providers/common/der/build.info +++ b/providers/common/der/build.info @@ -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 index 00000000000..43abbd12918 --- /dev/null +++ b/providers/common/der/der_hkdf_gen.c.in @@ -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 index 00000000000..b0820ed10fc --- /dev/null +++ b/providers/common/include/prov/der_hkdf.h.in @@ -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 }); +-} diff --git a/providers/implementations/keymgmt/build.info b/providers/implementations/keymgmt/build.info index f5ae1ece886..45221253503 100644 --- a/providers/implementations/keymgmt/build.info +++ b/providers/implementations/keymgmt/build.info @@ -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 diff --git a/providers/implementations/keymgmt/ml_kem_kmgmt.c.in b/providers/implementations/keymgmt/ml_kem_kmgmt.c.in index 9bab2f3b438..8d68128406d 100644 --- a/providers/implementations/keymgmt/ml_kem_kmgmt.c.in +++ b/providers/implementations/keymgmt/ml_kem_kmgmt.c.in @@ -19,9 +19,13 @@ use OpenSSL::paramnames qw(produce_param_decoder); #include #include #include +#include #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; } diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t index 3feea1e48e1..ea19f3b2eac 100644 --- a/test/recipes/80-test_cms.t +++ b/test/recipes/80-test_cms.t @@ -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"); + } +}; diff --git a/test/smime-certs/ca.cnf b/test/smime-certs/ca.cnf index 1bdffadf672..2cbf735bd24 100644 --- a/test/smime-certs/ca.cnf +++ b/test/smime-certs/ca.cnf @@ -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 diff --git a/test/smime-certs/mksmime-certs.sh b/test/smime-certs/mksmime-certs.sh index d989683faae..172e82cd5aa 100755 --- a/test/smime-certs/mksmime-certs.sh +++ b/test/smime-certs/mksmime-certs.sh @@ -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 index 00000000000..5e7b8bf844e --- /dev/null +++ b/test/smime-certs/sm_mlkem512.pem @@ -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 index 00000000000..be71ad0a694 --- /dev/null +++ b/test/smime-certs/sm_mlkem768.pem @@ -0,0 +1,94 @@ +-----BEGIN PRIVATE KEY----- +MIIJvgIBADALBglghkgBZQMEBAIEggmqMIIJpgRAwM7OMn3NSdREZtO/1O4mHNM4 +Z7pDPeZZHCevPXEa+dlZvdt4KChgP4YogG+Nf1m+kxrIlz55JcvmfHSsOZpt7wSC +CWCtSYlIFEDlmsqqMD7+RSQAESyMsxrNEzzZ2yafcl0WwrkyF5MdOarJ08pZe512 +QiUdUEqMJ7NwJZdK9HVPMT4yVCHe4FdDkBJmqaDgmroO+oABkJvToAnx1mrQ1h1F +wnPLgaTXICLftJhBe12k0W8Kpcgpe4+h53cm6sc1qGqV/Lcdk8cOTAwW+7Q5ZLtB +eFL2AKjEC1ulhC7VtleQB77dtKrGeb/zXLh3JWEZ8XP6VUrdJblTZCwhBhDvZW6w +6gk3HEfMkT3OxQvXRMhulRsKpoy2C1GcPFBdBzJ56AyCtaCNls4DKAkHe3vPZ8Ar +M1qCBFrJawnqUDxBMG1yQjYvxScFg7TMaWPgWLg5mRQgh5rElV5x+Fr2GVjaEA2A +YUdLmjjgcw8IMzFHiiD/640wAnXQl4jVxMsgpaQ9ibKCVRoG+Lk4+QVLkLjEml3A +UGtXoq2Q1UppmM4SlkPPLGEaW0wawa8Op88aI4+dpmi8PJKxBakFXMfXmHkumZYm +MheFwDR7aM4FyJM8cLajIwz4MUDH4x4k+qIxcAWSRDAoml3QaBMC68Oio5T9Vjfm +QQf0+KEVgW0JEYaSvJZbcrBH0SA4hrSIFA7Km3uOuHxDSRWMyjut4aoe9Esd+TVo +bLzbtlKF+hj4J4OrBAC5NBagyilKy1Cz2lv/VFJ/MFZ1lSt9p3i+ChsCh6iaZpt9 +dHm49C8zESq2YaSSJiTzVFjZOk57TD0OVCO3+k1o2FFZDLNEsJeICJSs6VgAGwOj +AWQYsE1UNnS8NRxd3JUTOCxk+nhfp7nqyDTfUjvNJWe5GFkj83SK4MLtpouzNLE1 +CEVtcwxqNR8OTMCeXMfIB6rwllJJl5Zki3pqs6UTe4XAl4BaELnsU0NPqaIc0KjX +2qW3BBECtzxSGBFvIacbto2So4G/BI+VaHoPwKfKnDNxUxf5G3wjeqL+Qa2UAVtg +F1uXaLl7LGBNBBFrDBPVnGvsaRhBOXwFicIkpsOga3J223kuElobi07gEQbTBUTO +CCTjQcHIegPJCgqTe8E/KgJ1EMxO9JjTiHMtmrJb8iki7EinaB+FMkbgcljkGzxn +NahPU4R2mH306XBsipRAIyhQgW8ncESSOgbSrGj6wLFRtqNAcKQptYWeC6Rm91VL +GTH2lFyJuj9FhzGx5sy0QFH2alcwAF5qK1ciIBXC8CHMWjr3qApDnEubml2rErZB +4MTQMF53yYVZaJOiwBiTbEtQdgtY+m0LkwEurMR52HL9phaYlB+UPE9AZFTtFIlx +y6UX1o7kEMv32ztmGIXnWETKqDqOrGUI2Xk0IjYmQc6gK3prWUKGtDywVrEGYpOI +erNamXzfR4TNRLesBIDL8CIcoWFNBSd7E01EMADf1m5TdGwa5Y3964Qz0hYpwgTt +sQLqMg84phdF+qzgXLBNG3nZZ5US1XmdZmVE1lf40XusSp+YScDS0XPNIZOgAMEg +kTi0aSJMcxLP2oDMWcJz2lCqGr3xiFglhR9FkIygsrao5Xp3t7YIE6duICZorBpV +ciKkd2437A3nMhMFSqkWyCb8UD1+87ErybV8B4MYNgLz8SLMGAwegG+fhQll9zFW +mAIY6lAuMihM8zopSnfwdzWnuVUGcr+wAKr/MwTrSZt1iQrd+YouWLvp3CeW3L6C +tgSWVlOEs1D16QOf+pIfuagoe6hpY3pl6I1EJSogNQEYNokdAZpQHEu9M5Nr97i3 +ZG3W2Rcs53/JaGt2xTt9NQhfioTHeq4Q1D2hUyqG1YUFKMbvhH3j0ZYfWrMmBIfK +NDvZtG4yQbrbhAt02VrRVW3C5k+8mkTcKjDZcwKylw7iIcayOXG9IzBD8Uq4eQxb +V0Rj3I0e0mV8OJ4PJcs0DDjc0YSEEc+mqbGl4nqx7MU+4himu3reZ4grCJF1y4up +ijpkMLow9p9VWgIsgX8iAaJ1iByhhZNy009sBcpjsLzUuk17Bn2UJFsR0ZBicTvG +cUW5FxJ6cr6RZ2712z/gVk9AK7qzp8rC53gKq4/pUWd+UgPFTJnoO2woOnfu1HR5 +bLREGoaCJ4XORZvhS4QSW6q1Uyo4wa5JsTzr2G+tLD1y2omI2jEzkr1TdM42ALTA ++LeT6wffpLboWBFOY2uXAAF9dUJjdweBQVmWKrkaLK0ZE8u3tA3CwHlNYkPvmnUB +l1TdYbMaHA0vJEwMlC/MRZwEU7CQ84ujo5nwkBdjEY/IYKD1xIN72UYUGDlD1B6T ++UELpkYRJG5798pFEjXUtZlgA56YEIvHYHUx+AZm6pD+qwGD5hi4qqTjAQY8qojU +iQufLJdqSDvQQYcMjCtMyMHmQZ2TIp4GQUZjEFNY858c3L6NQFcMCJ2kciIvlYwP +ZUCkdUlO+pe1ay4S6JjEiUJT5rdQm29OWknDwXQ0SWdbOgA7mQy5BaGbBGlmJHG2 +dikbY26F4b7qVM4ZIlmZKcQz0DI6wHoQQsFOezObG3hp6iKGrEG2RMDLq5oxWTXy +6M6jVG26h6W1iAwYhbrIYWIll2JMwWAAU7zvzBrxCLQmfF8yeHKvYmYvCcHJ8VBQ +6syrmn81Msr0cRzMBldSPI228E6HqG8JnDrO22LCqTIFgZS9eb311XSRuU/bxJWX +jFAWs0M2a7hP5AVAdYsLUDsb9cnh5L7LEqC30szFEgVQA7F/XHtnVZmPVYDkMIs9 +BAy3JsCHR4wJnF0M4bqLeskYQEJp0nqktXtSnDM7rAT2Wo5lMi7tlBlqPEVlUsm9 +ZUzRBUncgkcSpqqKgIJU0bLVUq+/YTMydgx3aqMXAoPgCSAQc50+XD28/D7xUMmU +9YWMUcQhEE0eaYl9CXD5h4YSIb+E5FogsY+XsJF66H/jYxGP11q5IhAL1CYyq4js +Bj9cx8d8RmUc4jrkxim+4YW8h0RBbEe6pwBdGnEhmnI29T4hYKYR+aJBB6qmIA6C +Rp9BKREPYV5PcFwZ94qH8n4DOFof6hio3EGihm5U+4ygMnQdLIyFUmOt+Y9Lq4W0 +4SGC5kMpRgnPKnNbic2Hd18tNg3vI6W2SYSehBKD8MYy/FtaEzZxQBJy5JUoEcXt +aIsGVMRZRV1FLlELAPjgFbx7jPTNme5RiKt6FEKsAMpBbCoJRpycN3FW5JhZmuaj +9Cx3xNeUrTJKodO0c/NELCTJWb3beCgoYD+GKIBvjX9ZvpMayJc+eSXL5nx0rDma +be8= +-----END PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIG5DCCBcygAwIBAgIUTkdeOIWbM/e6U4afCgsIGXQVSBMwDQYJKoZIhvcNAQEL +BQAwRDELMAkGA1UEBhMCVUsxFjAUBgNVBAoMDU9wZW5TU0wgR3JvdXAxHTAbBgNV +BAMMFFRlc3QgUy9NSU1FIFJTQSBSb290MCAXDTI1MDQwMjE0MTc1NFoYDzIxMjUw +NDAyMTQxNzU0WjAjMSEwHwYDVQQDDBhUZXN0IFNNSU1FIEVFIE1MLUtFTS03Njgw +ggSyMAsGCWCGSAFlAwQEAgOCBKEApHduN+wN5zITBUqpFsgm/FA9fvOxK8m1fAeD +GDYC8/EizBgMHoBvn4UJZfcxVpgCGOpQLjIoTPM6KUp38Hc1p7lVBnK/sACq/zME +60mbdYkK3fmKLli76dwnlty+grYEllZThLNQ9ekDn/qSH7moKHuoaWN6ZeiNRCUq +IDUBGDaJHQGaUBxLvTOTa/e4t2Rt1tkXLOd/yWhrdsU7fTUIX4qEx3quENQ9oVMq +htWFBSjG74R949GWH1qzJgSHyjQ72bRuMkG624QLdNla0VVtwuZPvJpE3Cow2XMC +spcO4iHGsjlxvSMwQ/FKuHkMW1dEY9yNHtJlfDieDyXLNAw43NGEhBHPpqmxpeJ6 +sezFPuIYprt63meIKwiRdcuLqYo6ZDC6MPafVVoCLIF/IgGidYgcoYWTctNPbAXK +Y7C81LpNewZ9lCRbEdGQYnE7xnFFuRcSenK+kWdu9ds/4FZPQCu6s6fKwud4CquP +6VFnflIDxUyZ6DtsKDp37tR0eWy0RBqGgieFzkWb4UuEEluqtVMqOMGuSbE869hv +rSw9ctqJiNoxM5K9U3TONgC0wPi3k+sH36S26FgRTmNrlwABfXVCY3cHgUFZliq5 +GiytGRPLt7QNwsB5TWJD75p1AZdU3WGzGhwNLyRMDJQvzEWcBFOwkPOLo6OZ8JAX +YxGPyGCg9cSDe9lGFBg5Q9Qek/lBC6ZGESRue/fKRRI11LWZYAOemBCLx2B1MfgG +ZuqQ/qsBg+YYuKqk4wEGPKqI1IkLnyyXakg70EGHDIwrTMjB5kGdkyKeBkFGYxBT +WPOfHNy+jUBXDAidpHIiL5WMD2VApHVJTvqXtWsuEuiYxIlCU+a3UJtvTlpJw8F0 +NElnWzoAO5kMuQWhmwRpZiRxtnYpG2NuheG+6lTOGSJZmSnEM9AyOsB6EELBTnsz +mxt4aeoihqxBtkTAy6uaMVk18ujOo1RtuoeltYgMGIW6yGFiJZdiTMFgAFO878wa +8Qi0JnxfMnhyr2JmLwnByfFQUOrMq5p/NTLK9HEczAZXUjyNtvBOh6hvCZw6ztti +wqkyBYGUvXm99dV0kblP28SVl4xQFrNDNmu4T+QFQHWLC1A7G/XJ4eS+yxKgt9LM +xRIFUAOxf1x7Z1WZj1WA5DCLPQQMtybAh0eMCZxdDOG6i3rJGEBCadJ6pLV7Upwz +O6wE9lqOZTIu7ZQZajxFZVLJvWVM0QVJ3IJHEqaqioCCVNGy1VKvv2EzMnYMd2qj +FwKD4AkgEHOdPlw9vPw+8VDJlPWFjFHEIRBNHmmJfQlw+YeGEiG/hORaILGPl7CR +euh/42MRj9dauSIQC9QmMquI7AY/XMfHfEZlHOI65MYpvuGFvIdEQWxHuqcAXRpx +IZpyNvU+IWCmEfmiQQeqpiAOgkafQSkRD2FeT3BcGfeKh/J+AzhaH+oYqNxBooZu +VPuMoDJ0HSyMhVJjrfmPS6uFtOEhguZDKUYJzypzW4nNh3dfLTYN7yOltkmEnoQS +g/DGMvxbWhM2cUAScuSVKBHF7WiLBlTEWUVdRS5RCwD44BW8e4z0zZnuUYirehRC +rADKQWyjXTBbMAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgUgMB0GA1UdDgQWBBSa +EG86QJUCy7yinxZ4JNZfRGcruDAfBgNVHSMEGDAWgBQVwRMha+JVX6dqHVcg1s/z +qXNkWTANBgkqhkiG9w0BAQsFAAOCAQEAl98fiNkFW2HGFdxBl3wyz6GfjjeQZJru +Zi/l/xaT/6cOoCngs7zb0UHKhh/T/0KqRHVN99Z0RLqvfnH6c5wglzdPEZMf5I+/ +ceQ0DNJ5hSf852MBivp59LnYeXvL+lbGGdLTztBB408HB3zWQJkWdYAzzr3573NW +y9c9LHbdeSy+FUiKNB3QV0vbY4JwCAtzvRc5KElwRz9Hfg32f5v+xreWjKzshW7y +o3a6dQmthuQ4doWfZJDl2FSZHa2NAVM/UDgm9PeFlAtRHGhqEku/GK5McuVwWg+9 +WppYreBcT/hkqYWibme28kAm3XWpXHOcpOtAbkvgRE2WaPFhBkqRRA== +-----END CERTIFICATE----- diff --git a/util/indent.pro b/util/indent.pro index bc626e4a4bc..1937cfa476f 100644 --- a/util/indent.pro +++ b/util/indent.pro @@ -123,6 +123,7 @@ -T CMAC_CTX -T CMS_AuthenticatedData -T CMS_CertificateChoices +-T CMS_CMSORIforKEMOtherInfo -T CMS_CompressedData -T CMS_ContentInfo -T CMS_DigestedData @@ -133,6 +134,7 @@ -T CMS_IssuerAndSerialNumber -T CMS_KEKIdentifier -T CMS_KEKRecipientInfo +-T CMS_KEMRecipientInfo -T CMS_KeyAgreeRecipientIdentifier -T CMS_KeyAgreeRecipientInfo -T CMS_KeyTransRecipientInfo diff --git a/util/libcrypto.num b/util/libcrypto.num index 2228aa3bf60..d1667ee2a67 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -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 diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt index 9191854ae77..ed586f8e30e 100644 --- a/util/missingcrypto.txt +++ b/util/missingcrypto.txt @@ -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) diff --git a/util/perl/OpenSSL/paramnames.pm b/util/perl/OpenSSL/paramnames.pm index bcb27cf4f37..203c6f73b31 100644 --- a/util/perl/OpenSSL/paramnames.pm +++ b/util/perl/OpenSSL/paramnames.pm @@ -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",