From c07aac0a0dced669f5291aad98c453f3d47eab10 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 7 Apr 2020 16:36:32 +0300 Subject: [PATCH 54/87] cms: add support for using AEAD ciphers in CMS files Russian standards body has added definition for special unprotected attribute (id-cms-mac-attr) used together with AEAD ciphers (primarily CTR-ACKM-OMAC) to store MAC value together with encrypted data. Sponsored by ROSA Linux Signed-off-by: Dmitry Baryshkov --- src/lib/libcrypto/cms/cms_enc.c | 66 ++++++++++++++++++++++++++- src/lib/libcrypto/cms/cms_env.c | 15 +++++- src/lib/libcrypto/cms/cms_lcl.h | 7 ++- src/lib/libcrypto/cms/cms_lib.c | 8 +++- src/lib/libcrypto/objects/obj_mac.num | 1 + src/lib/libcrypto/objects/objects.txt | 1 + 6 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/lib/libcrypto/cms/cms_enc.c b/src/lib/libcrypto/cms/cms_enc.c index fd2df99c6..2b8f9c5b8 100644 --- a/src/lib/libcrypto/cms/cms_enc.c +++ b/src/lib/libcrypto/cms/cms_enc.c @@ -68,7 +68,8 @@ /* Return BIO based on EncryptedContentInfo and key */ BIO * -cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec) +cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec, + STACK_OF(X509_ATTRIBUTE) *unprotectedAttrs) { BIO *b; EVP_CIPHER_CTX *ctx; @@ -189,6 +190,23 @@ cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec) ASN1_TYPE_free(calg->parameter); calg->parameter = NULL; } + } else if (ciph->flags & EVP_CIPH_FLAG_AEAD_CIPHER) { + int idx = X509at_get_attr_by_NID(unprotectedAttrs, NID_id_cms_mac_attr, -1); + X509_ATTRIBUTE *attr; + ASN1_TYPE *type; + + if (idx == -1 || + (attr = X509at_get_attr(unprotectedAttrs, idx)) == NULL || + attr->single != 0 || + sk_ASN1_TYPE_num(attr->value.set) != 1 || + (type = sk_ASN1_TYPE_value(attr->value.set, 0)) == NULL || + type->type != V_ASN1_OCTET_STRING || + !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, + type->value.octet_string->length, + type->value.octet_string->data)) { + CMSerror(CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR); + goto err; + } } ok = 1; @@ -204,6 +222,43 @@ cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec) return NULL; } +int cms_EncryptedContent_final(CMS_EncryptedContentInfo *ec, + BIO *chain, STACK_OF(X509_ATTRIBUTE) **unprotectedAttrs) +{ + EVP_CIPHER_CTX *ctx = NULL; + BIO *mbio = BIO_find_type(chain, BIO_TYPE_CIPHER); + + if (mbio == NULL) { + CMSerror(CMS_R_CONTENT_NOT_FOUND); + return 0; + } + + BIO_get_cipher_ctx(mbio, &ctx); + if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) { + unsigned char tag[EVP_MAX_MD_SIZE]; + int taglen; + int nid = EVP_CIPHER_CTX_nid(ctx); + + /* Most of the AEAD ciphers have 16-bit tags except Magma ciphers */ + if (nid == NID_id_tc26_cipher_gostr3412_2015_magma_ctracpkm_omac || + nid == NID_id_tc26_cipher_gostr3412_2015_magma_mgm) + taglen = 8; + else + taglen = 16; + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, + taglen, tag) <= 0) { + CMSerror(CMS_R_CTRL_FAILURE); + return 0; + } + + if (!X509at_add1_attr_by_NID(unprotectedAttrs, NID_id_cms_mac_attr, V_ASN1_OCTET_STRING, tag, taglen)) + return 0; + } + + return 1; +} + int cms_EncryptedContent_init(CMS_EncryptedContentInfo *ec, const EVP_CIPHER *cipher, const unsigned char *key, size_t keylen) @@ -258,5 +313,12 @@ cms_EncryptedData_init_bio(CMS_ContentInfo *cms) if (enc->encryptedContentInfo->cipher && enc->unprotectedAttrs) enc->version = 2; - return cms_EncryptedContent_init_bio(enc->encryptedContentInfo); + return cms_EncryptedContent_init_bio(enc->encryptedContentInfo, enc->unprotectedAttrs); +} + +int cms_EncryptedData_final(CMS_ContentInfo *cms, BIO *chain) +{ + CMS_EncryptedData *enc = cms->d.encryptedData; + + return cms_EncryptedContent_final(enc->encryptedContentInfo, chain, &enc->unprotectedAttrs); } diff --git a/src/lib/libcrypto/cms/cms_env.c b/src/lib/libcrypto/cms/cms_env.c index 911f7a8a2..f5e9280d4 100644 --- a/src/lib/libcrypto/cms/cms_env.c +++ b/src/lib/libcrypto/cms/cms_env.c @@ -927,7 +927,7 @@ cms_EnvelopedData_init_bio(CMS_ContentInfo *cms) /* Get BIO first to set up key */ ec = cms->d.envelopedData->encryptedContentInfo; - ret = cms_EncryptedContent_init_bio(ec); + ret = cms_EncryptedContent_init_bio(ec, cms->d.envelopedData->unprotectedAttrs); /* If error or no cipher end of processing */ @@ -960,6 +960,19 @@ cms_EnvelopedData_init_bio(CMS_ContentInfo *cms) return NULL; } +int cms_EnvelopedData_final(CMS_ContentInfo *cms, BIO *chain) +{ + CMS_EnvelopedData *env = NULL; + + env = cms_get0_enveloped(cms); + if (env == NULL) { + CMSerror(CMS_R_CONTENT_TYPE_NOT_ENVELOPED_DATA); + return 0; + } + + return cms_EncryptedContent_final(env->encryptedContentInfo, chain, &env->unprotectedAttrs); +} + /* * Get RecipientInfo type (if any) supported by a key (public or private). To * retain compatibility with previous behaviour if the ctrl value isn't diff --git a/src/lib/libcrypto/cms/cms_lcl.h b/src/lib/libcrypto/cms/cms_lcl.h index 8083e5537..fdfd12160 100644 --- a/src/lib/libcrypto/cms/cms_lcl.h +++ b/src/lib/libcrypto/cms/cms_lcl.h @@ -442,16 +442,21 @@ int cms_keyid_cert_cmp(ASN1_OCTET_STRING *keyid, X509 *cert); int cms_set1_ias(CMS_IssuerAndSerialNumber **pias, X509 *cert); int cms_set1_keyid(ASN1_OCTET_STRING **pkeyid, X509 *cert); -BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec); +BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec, + STACK_OF(X509_ATTRIBUTE) *unprotectedAttrs); BIO *cms_EncryptedData_init_bio(CMS_ContentInfo *cms); +int cms_EncryptedData_final(CMS_ContentInfo *cms, BIO *chain); int cms_EncryptedContent_init(CMS_EncryptedContentInfo *ec, const EVP_CIPHER *cipher, const unsigned char *key, size_t keylen); +int cms_EncryptedContent_final(CMS_EncryptedContentInfo *ec, + BIO *chain, STACK_OF(X509_ATTRIBUTE) **unprotectedAttrs); int cms_Receipt_verify(CMS_ContentInfo *cms, CMS_ContentInfo *req_cms); int cms_msgSigDigest_add1(CMS_SignerInfo *dest, CMS_SignerInfo *src); ASN1_OCTET_STRING *cms_encode_Receipt(CMS_SignerInfo *si); BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms); +int cms_EnvelopedData_final(CMS_ContentInfo *cms, BIO *chain); CMS_EnvelopedData *cms_get0_enveloped(CMS_ContentInfo *cms); int cms_env_asn1_ctrl(CMS_RecipientInfo *ri, int cmd); int cms_pkey_get_ri_type(EVP_PKEY *pk); diff --git a/src/lib/libcrypto/cms/cms_lib.c b/src/lib/libcrypto/cms/cms_lib.c index b6580dd6f..787a8f99e 100644 --- a/src/lib/libcrypto/cms/cms_lib.c +++ b/src/lib/libcrypto/cms/cms_lib.c @@ -215,12 +215,16 @@ CMS_dataFinal(CMS_ContentInfo *cms, BIO *cmsbio) switch (OBJ_obj2nid(cms->contentType)) { case NID_pkcs7_data: - case NID_pkcs7_enveloped: - case NID_pkcs7_encrypted: case NID_id_smime_ct_compressedData: /* Nothing to do */ return 1; + case NID_pkcs7_enveloped: + return cms_EnvelopedData_final(cms, cmsbio); + + case NID_pkcs7_encrypted: + return cms_EncryptedData_final(cms, cmsbio); + case NID_pkcs7_signed: return cms_SignedData_final(cms, cmsbio); diff --git a/src/lib/libcrypto/objects/obj_mac.num b/src/lib/libcrypto/objects/obj_mac.num index 188349d71..6e494cb92 100644 --- a/src/lib/libcrypto/objects/obj_mac.num +++ b/src/lib/libcrypto/objects/obj_mac.num @@ -1020,3 +1020,4 @@ id_tc26_agreement_gost_3410_12_256 1019 id_tc26_agreement_gost_3410_12_512 1020 id_tc26_wrap_gostr3412_2015_magma_kexp15 1021 id_tc26_wrap_gostr3412_2015_kuznyechik_kexp15 1022 +id_cms_mac_attr 1023 diff --git a/src/lib/libcrypto/objects/objects.txt b/src/lib/libcrypto/objects/objects.txt index 0d2af66ac..3f810dcf8 100644 --- a/src/lib/libcrypto/objects/objects.txt +++ b/src/lib/libcrypto/objects/objects.txt @@ -1368,6 +1368,7 @@ brainpool 1 14 : brainpoolP512t1 : gost89-cbc member-body 643 7 1 : tc26 +tc26 0 6 1 1 : id-cms-mac-attr !Cname id-tc26-gost3411-2012-256 tc26 1 2 2 : streebog256 : GOST R 34.11-2012 (256 bit) !Cname id-tc26-gost3411-2012-512 -- 2.17.1