Commit f3867bb25b for openssl.org
commit f3867bb25bee6267eb292ebdb0528de17710828f
Author: Jakub Zelenka <jakub.openssl@gmail.com>
Date: Thu Feb 6 19:07:28 2025 +0100
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 <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26871)
diff --git a/apps/cms.c b/apps/cms.c
index 8c7feed431..0a2ce7f571 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 d907ad9a57..efed89a017 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 36f1b3e4a8..30e7734d0f 100644
--- a/doc/man1/openssl-cms.pod.in
+++ b/doc/man1/openssl-cms.pod.in
@@ -68,6 +68,7 @@ Encryption options:
[B<-recip> I<file>]
[I<recipient-cert> ...]
[B<-I<cipher>>]
+[B<-kekcipher> I<cipher>]
[B<-wrap> I<cipher>]
[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>
+
+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>
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 6e3508dac8..3feea1e48e 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",