From: Jakub Zelenka Date: Thu, 6 Feb 2025 18:07:28 +0000 (+0100) Subject: Introduce cms kekcipher option to select cipher for pwri X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f3867bb25bee6267eb292ebdb0528de17710828f;p=thirdparty%2Fopenssl.git Introduce cms kekcipher option to select cipher for pwri This is useful for AEAD ciphers where it is not possible to use AEAD cipher (currently only AES GCM supported) for password recipient info because the same cipher is used for encrypting the password and it is not possible to store tag for this purpose so different cipher (e.g. AES CBC) needs to be selected. Reviewed-by: Shane Lontis Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/26871) --- diff --git a/apps/cms.c b/apps/cms.c index 8c7feed4312..0a2ce7f571f 100644 --- a/apps/cms.c +++ b/apps/cms.c @@ -84,7 +84,7 @@ typedef enum OPTION_choice { OPT_R_ENUM, OPT_PROV_ENUM, OPT_CONFIG, OPT_V_ENUM, - OPT_CIPHER, + OPT_CIPHER, OPT_KEKCIPHER, OPT_ORIGINATOR } OPTION_CHOICE; @@ -164,6 +164,8 @@ const OPTIONS cms_options[] = { "Recipient certs (optional; used only when encrypting)"}, {"", OPT_CIPHER, '-', "The encryption algorithm to use (any supported cipher)"}, + {"kekcipher", OPT_KEKCIPHER, 's', + "The key encryption algorithm to use"}, {"wrap", OPT_WRAP, 's', "Key wrap algorithm to use when encrypting with key agreement"}, {"aes128-wrap", OPT_AES128_WRAP, '-', "Use AES128 to wrap key"}, @@ -288,7 +290,7 @@ int cms_main(int argc, char **argv) CMS_ReceiptRequest *rr = NULL; ENGINE *e = NULL; EVP_PKEY *key = NULL; - EVP_CIPHER *cipher = NULL, *wrap_cipher = NULL; + EVP_CIPHER *cipher = NULL, *wrap_cipher = NULL, *kekcipher = NULL; EVP_MD *sign_md = NULL; STACK_OF(OPENSSL_STRING) *rr_to = NULL, *rr_from = NULL; STACK_OF(OPENSSL_STRING) *sksigners = NULL, *skkeys = NULL; @@ -305,7 +307,8 @@ int cms_main(int argc, char **argv) long digestlen = 0; char *infile = NULL, *outfile = NULL, *rctfile = NULL; char *passinarg = NULL, *passin = NULL, *signerfile = NULL; - char *originatorfile = NULL, *recipfile = NULL, *ciphername = NULL; + char *originatorfile = NULL, *recipfile = NULL; + char *ciphername = NULL, *kekciphername = NULL; char *to = NULL, *from = NULL, *subject = NULL, *prog; cms_key_param *key_first = NULL, *key_param = NULL; int flags = CMS_DETACHED, binary_files = 0; @@ -653,6 +656,9 @@ int cms_main(int argc, char **argv) case OPT_CIPHER: ciphername = opt_unknown(); break; + case OPT_KEKCIPHER: + kekciphername = opt_arg(); + break; case OPT_KEYOPT: keyidx = -1; if (operation == SMIME_ENCRYPT) { @@ -724,6 +730,8 @@ int cms_main(int argc, char **argv) } if (!opt_cipher_any(ciphername, &cipher)) goto end; + if (kekciphername != NULL && !opt_cipher_any(kekciphername, &kekcipher)) + goto end; if (wrapname != NULL) { if (!opt_cipher_any(wrapname, &wrap_cipher)) goto end; @@ -1033,9 +1041,8 @@ int cms_main(int argc, char **argv) pwri_tmp = (unsigned char *)OPENSSL_strdup((char *)pwri_pass); if (pwri_tmp == NULL) goto end; - if (CMS_add0_recipient_password(cms, - -1, NID_undef, NID_undef, - pwri_tmp, -1, NULL) == NULL) + if (CMS_add0_recipient_password(cms, -1, NID_undef, NID_undef, + pwri_tmp, -1, kekcipher) == NULL) goto end; pwri_tmp = NULL; } @@ -1315,6 +1322,7 @@ int cms_main(int argc, char **argv) X509_free(originator); EVP_PKEY_free(key); EVP_CIPHER_free(cipher); + EVP_CIPHER_free(kekcipher); EVP_CIPHER_free(wrap_cipher); EVP_MD_free(sign_md); CMS_ContentInfo_free(cms); diff --git a/crypto/cms/cms_pwri.c b/crypto/cms/cms_pwri.c index d907ad9a57a..efed89a017a 100644 --- a/crypto/cms/cms_pwri.c +++ b/crypto/cms/cms_pwri.c @@ -71,6 +71,10 @@ CMS_RecipientInfo *CMS_add0_recipient_password(CMS_ContentInfo *cms, ERR_raise(ERR_LIB_CMS, CMS_R_NO_CIPHER); return NULL; } + if ((EVP_CIPHER_get_flags(kekciph) & EVP_CIPH_FLAG_AEAD_CIPHER) != 0) { + ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_KEK_ALGORITHM); + return NULL; + } if (wrap_nid != NID_id_alg_PWRI_KEK) { ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM); return NULL; diff --git a/doc/man1/openssl-cms.pod.in b/doc/man1/openssl-cms.pod.in index 36f1b3e4a82..30e7734d0f5 100644 --- a/doc/man1/openssl-cms.pod.in +++ b/doc/man1/openssl-cms.pod.in @@ -68,6 +68,7 @@ Encryption options: [B<-recip> I] [I ...] [B<-I>] +[B<-kekcipher> I] [B<-wrap> I] [B<-aes128-wrap>] [B<-aes192-wrap>] @@ -426,6 +427,13 @@ algorithms. If not specified, AES-256-CBC is used as the default. Only used with B<-encrypt> and B<-EncryptedData_create> commands. +=item B<-kekcipher> I + +Cipher algorithm to use for password key encryption. This option is relevant +when a password for password recipient information is provided using +B<-pwri_password>, and an AEAD encryption algorithm is selected. In such cases, +a non-AEAD algorithm should be specified using this option. + =item B<-wrap> I Cipher algorithm to use for key wrap when encrypting the message using Key diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t index 6e3508dac8f..3feea1e48e1 100644 --- a/test/recipes/80-test_cms.t +++ b/test/recipes/80-test_cms.t @@ -346,6 +346,17 @@ my @smime_cms_tests = ( \&final_compare ], + [ "enveloped content test streaming PEM format, AES-128-GCM cipher and AES-128-CBC KEK cipher, password", + [ "{cmd1}", @prov, "-encrypt", "-in", $smcont, "-outform", "PEM", "-aes-128-gcm", + "-kekcipher", "aes-128-cbc", + "-stream", "-out", "{output}.cms", + "-pwri_password", "test" ], + [ "{cmd2}", "-decrypt", "-in", "{output}.cms", "-out", "{output}.txt", + "-inform", "PEM", + "-pwri_password", "test" ], + \&final_compare + ], + [ "enveloped content test streaming PEM format, KEK, key only", [ "{cmd1}", @prov, "-encrypt", "-in", $smcont, "-outform", "PEM", "-aes128", "-stream", "-out", "{output}.cms",