From 4f8119d9168bfe32b03d31673425423d466f365c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 9 Apr 2020 03:04:33 +0300 Subject: [PATCH 63/87] cms: support specifying originator certificate and key during encryption LibreSSL supports using ephemeral public key as Originator. Add support for using other Originator types. Sponsored by ROSA Linux Signed-off-by: Dmitry Baryshkov --- src/lib/libcrypto/cms/cms.h | 6 +++- src/lib/libcrypto/cms/cms_env.c | 6 ++-- src/lib/libcrypto/cms/cms_kari.c | 45 +++++++++++++++++++++++-- src/lib/libcrypto/cms/cms_lcl.h | 3 +- src/lib/libcrypto/cms/cms_smime.c | 3 +- src/lib/libcrypto/ec/ec_ameth.c | 5 ++- src/regress/lib/libcrypto/cms/cmstest.c | 2 +- src/usr.bin/openssl/cms.c | 25 ++++++++++++-- 8 files changed, 83 insertions(+), 12 deletions(-) diff --git a/src/lib/libcrypto/cms/cms.h b/src/lib/libcrypto/cms/cms.h index 0c62536ec..4084fd7da 100644 --- a/src/lib/libcrypto/cms/cms.h +++ b/src/lib/libcrypto/cms/cms.h @@ -125,6 +125,7 @@ int CMS_ContentInfo_print_ctx(BIO *out, CMS_ContentInfo *x, int indent, const AS #define CMS_DEBUG_DECRYPT 0x20000 #define CMS_KEY_PARAM 0x40000 #define CMS_ASCIICRLF 0x80000 +#define CMS_USE_ORIGINATOR_KEYID 0x100000 const ASN1_OBJECT *CMS_get0_type(const CMS_ContentInfo *cms); @@ -188,7 +189,9 @@ int CMS_verify_receipt(CMS_ContentInfo *rcms, CMS_ContentInfo *ocms, STACK_OF(X509) *CMS_get0_signers(CMS_ContentInfo *cms); CMS_ContentInfo *CMS_encrypt(STACK_OF(X509) *certs, BIO *in, - const EVP_CIPHER *cipher, unsigned int flags); + const EVP_CIPHER *cipher, + EVP_PKEY *originator_pkey, X509 *originator, + unsigned int flags); int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pkey, X509 *cert, BIO *dcont, BIO *out, unsigned int flags); @@ -205,6 +208,7 @@ int CMS_RecipientInfo_type(CMS_RecipientInfo *ri); EVP_PKEY_CTX *CMS_RecipientInfo_get0_pkey_ctx(CMS_RecipientInfo *ri); CMS_ContentInfo *CMS_EnvelopedData_create(const EVP_CIPHER *cipher); CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms, X509 *recip, + EVP_PKEY *originator_pkey, X509 *originator, unsigned int flags); int CMS_RecipientInfo_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pkey); int CMS_RecipientInfo_ktri_cert_cmp(CMS_RecipientInfo *ri, X509 *cert); diff --git a/src/lib/libcrypto/cms/cms_env.c b/src/lib/libcrypto/cms/cms_env.c index 17778a4fe..b51baf702 100644 --- a/src/lib/libcrypto/cms/cms_env.c +++ b/src/lib/libcrypto/cms/cms_env.c @@ -238,7 +238,9 @@ cms_RecipientInfo_ktri_init(CMS_RecipientInfo *ri, X509 *recip, EVP_PKEY *pk, */ CMS_RecipientInfo * -CMS_add1_recipient_cert(CMS_ContentInfo *cms, X509 *recip, unsigned int flags) +CMS_add1_recipient_cert(CMS_ContentInfo *cms, X509 *recip, + EVP_PKEY *originator_pkey, X509 *originator, + unsigned int flags) { CMS_RecipientInfo *ri = NULL; CMS_EnvelopedData *env; @@ -267,7 +269,7 @@ CMS_add1_recipient_cert(CMS_ContentInfo *cms, X509 *recip, unsigned int flags) break; case CMS_RECIPINFO_AGREE: - if (!cms_RecipientInfo_kari_init(ri, recip, pk, flags)) + if (!cms_RecipientInfo_kari_init(ri, recip, pk, originator, originator_pkey, flags)) goto err; break; diff --git a/src/lib/libcrypto/cms/cms_kari.c b/src/lib/libcrypto/cms/cms_kari.c index 145cc48dd..5a6e4b820 100644 --- a/src/lib/libcrypto/cms/cms_kari.c +++ b/src/lib/libcrypto/cms/cms_kari.c @@ -362,10 +362,33 @@ cms_kari_create_ephemeral_key(CMS_KeyAgreeRecipientInfo *kari, EVP_PKEY *pk) return rv; } +/* Set the key from originator */ +static int +cms_kari_set_originator_priv_key(CMS_KeyAgreeRecipientInfo *kari, EVP_PKEY *pk) +{ + EVP_PKEY_CTX *pctx = NULL; + int rv = 0; + + pctx = EVP_PKEY_CTX_new(pk, NULL); + if (!pctx) + goto err; + if (EVP_PKEY_derive_init(pctx) <= 0) + goto err; + kari->pctx = pctx; + rv = 1; + + err: + if (!rv) + EVP_PKEY_CTX_free(pctx); + + return rv; +} + /* Initialise a kari based on passed certificate and key */ int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip, EVP_PKEY *pk, + X509 *originator, EVP_PKEY *originator_pkey, unsigned int flags) { CMS_KeyAgreeRecipientInfo *kari; @@ -401,9 +424,25 @@ cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip, EVP_PKEY *pk, return 0; } - /* Create ephemeral key */ - if (!cms_kari_create_ephemeral_key(kari, pk)) - return 0; + if (originator_pkey == NULL && originator == NULL) { + /* Create ephemeral key */ + if (!cms_kari_create_ephemeral_key(kari, pk)) + return 0; + } else { + CMS_OriginatorIdentifierOrKey *oik = ri->d.kari->originator; + + if (flags & CMS_USE_ORIGINATOR_KEYID) { + oik->type = CMS_OIK_KEYIDENTIFIER; + if (!cms_set1_keyid(&oik->d.subjectKeyIdentifier, originator)) + return 0; + } else { + oik->type = CMS_OIK_ISSUER_SERIAL; + if (!cms_set1_ias(&oik->d.issuerAndSerialNumber, originator)) + return 0; + } + if (!cms_kari_set_originator_priv_key(kari, originator_pkey)) + return 0; + } EVP_PKEY_up_ref(pk); rek->pkey = pk; diff --git a/src/lib/libcrypto/cms/cms_lcl.h b/src/lib/libcrypto/cms/cms_lcl.h index fa6c7660c..e70c27e71 100644 --- a/src/lib/libcrypto/cms/cms_lcl.h +++ b/src/lib/libcrypto/cms/cms_lcl.h @@ -465,7 +465,8 @@ int cms_pkey_get_ri_type(EVP_PKEY *pk); int cms_pkey_is_ri_type_supported(EVP_PKEY *pk, int ri_type); /* KARI routines */ int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip, - EVP_PKEY *pk, unsigned int flags); + EVP_PKEY *pk, X509 *originator, EVP_PKEY *originator_pkey, + unsigned int flags); int cms_RecipientInfo_kari_encrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri); /* PWRI routines */ diff --git a/src/lib/libcrypto/cms/cms_smime.c b/src/lib/libcrypto/cms/cms_smime.c index 1a067c149..dd1eb787b 100644 --- a/src/lib/libcrypto/cms/cms_smime.c +++ b/src/lib/libcrypto/cms/cms_smime.c @@ -612,6 +612,7 @@ CMS_sign_receipt(CMS_SignerInfo *si, X509 *signcert, EVP_PKEY *pkey, CMS_ContentInfo * CMS_encrypt(STACK_OF(X509) *certs, BIO *data, const EVP_CIPHER *cipher, + EVP_PKEY *originator_pkey, X509 *originator, unsigned int flags) { CMS_ContentInfo *cms; @@ -623,7 +624,7 @@ CMS_encrypt(STACK_OF(X509) *certs, BIO *data, const EVP_CIPHER *cipher, goto merr; for (i = 0; i < sk_X509_num(certs); i++) { recip = sk_X509_value(certs, i); - if (!CMS_add1_recipient_cert(cms, recip, flags)) { + if (!CMS_add1_recipient_cert(cms, recip, originator_pkey, originator, flags)) { CMSerror(CMS_R_RECIPIENT_ERROR); goto err; } diff --git a/src/lib/libcrypto/ec/ec_ameth.c b/src/lib/libcrypto/ec/ec_ameth.c index 2e73bdd2f..a93a924bb 100644 --- a/src/lib/libcrypto/ec/ec_ameth.c +++ b/src/lib/libcrypto/ec/ec_ameth.c @@ -832,7 +832,10 @@ ecdh_cms_encrypt(CMS_RecipientInfo *ri) if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &talg, &pubkey, NULL, NULL, NULL)) goto err; - X509_ALGOR_get0(&aoid, NULL, NULL, talg); + if (talg == NULL) + aoid = NULL; + else + X509_ALGOR_get0(&aoid, NULL, NULL, talg); /* Is everything uninitialised? */ if (aoid == OBJ_nid2obj(NID_undef)) { diff --git a/src/regress/lib/libcrypto/cms/cmstest.c b/src/regress/lib/libcrypto/cms/cmstest.c index 466583ecb..ca2619fc8 100644 --- a/src/regress/lib/libcrypto/cms/cmstest.c +++ b/src/regress/lib/libcrypto/cms/cmstest.c @@ -143,7 +143,7 @@ test_cms_encrypt_decrypt() if ((bio_mem = BIO_new_mem_buf(cms_msg, -1)) == NULL) errx(1, "failed to create BIO for message"); - if ((ci = CMS_encrypt(certs, bio_mem, EVP_aes_256_cbc(), 0)) == NULL) { + if ((ci = CMS_encrypt(certs, bio_mem, EVP_aes_256_cbc(), NULL, NULL, 0)) == NULL) { fprintf(stderr, "FAIL: CMS_encrypt returned NULL\n"); ERR_print_errors_fp(stderr); goto failure; diff --git a/src/usr.bin/openssl/cms.c b/src/usr.bin/openssl/cms.c index ada4c7746..a16987e93 100644 --- a/src/usr.bin/openssl/cms.c +++ b/src/usr.bin/openssl/cms.c @@ -246,6 +246,8 @@ cms_main(int argc, char **argv) flags |= CMS_BINARY; else if (!strcmp(*args, "-keyid")) flags |= CMS_USE_KEYID; + else if (!strcmp(*args, "-origkeyid")) + flags |= CMS_USE_ORIGINATOR_KEYID; else if (!strcmp(*args, "-nosigs")) flags |= CMS_NOSIGS; else if (!strcmp(*args, "-no_content_verify")) @@ -590,6 +592,7 @@ cms_main(int argc, char **argv) BIO_printf(bio_err, "-signer file signer certificate file\n"); BIO_printf(bio_err, "-recip file recipient certificate file for decryption\n"); BIO_printf(bio_err, "-keyid use subject key identifier\n"); + BIO_printf(bio_err, "-origkeyid use originator's key identifier\n"); BIO_printf(bio_err, "-in file input file\n"); BIO_printf(bio_err, "-inform arg input format SMIME (default), PEM or DER\n"); BIO_printf(bio_err, "-inkey file input private key (if not signer or recipient)\n"); @@ -686,6 +689,9 @@ cms_main(int argc, char **argv) if (operation == SMIME_DECRYPT) { if (!keyfile) keyfile = recipfile; + } else if (operation == SMIME_ENCRYPT) { + if (!keyfile) + keyfile = certfile; } else if ((operation == SMIME_SIGN) || (operation == SMIME_SIGN_RECEIPT)) { if (!keyfile) @@ -797,8 +803,23 @@ cms_main(int argc, char **argv) cms = CMS_compress(in, -1, flags); } else if (operation == SMIME_ENCRYPT) { int i; + X509 *orig = NULL; + if (other) { + if (!key) { + BIO_puts(bio_err,"Must specify CMS originator private key\n"); + goto end; + } + if (sk_X509_num(other) != 1) { + BIO_puts(bio_err,"Must specify only CMS originator certificate\n"); + goto end; + } + orig = sk_X509_value(other, 0); + } else if (key) { + BIO_puts(bio_err,"Must specify only CMS originator certificate (-certfile)\n"); + goto end; + } flags |= CMS_PARTIAL; - cms = CMS_encrypt(NULL, in, cipher, flags); + cms = CMS_encrypt(NULL, in, cipher, key, orig, flags); if (cms == NULL) goto end; for (i = 0; i < sk_X509_num(encerts); i++) { @@ -812,7 +833,7 @@ cms_main(int argc, char **argv) break; } } - ri = CMS_add1_recipient_cert(cms, x, tflags); + ri = CMS_add1_recipient_cert(cms, x, key, orig, tflags); if (ri == NULL) goto end; if (kparam != NULL) { -- 2.17.1