openssl/crypto/cms/cms_sd.c
Dr. Stephen Henson 17c2764d2e CMS support for key agreeement recipient info.
Add hooks to support key agreement recipient info type (KARI) using
algorithm specific code in the relevant public key ASN1 method.
2013-07-17 21:45:00 +01:00

1032 lines
24 KiB
C

/* crypto/cms/cms_sd.c */
/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
* project.
*/
/* ====================================================================
* Copyright (c) 2008 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
*
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* licensing@OpenSSL.org.
*
* 5. Products derived from this software may not be called "OpenSSL"
* nor may "OpenSSL" appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*/
#include "cryptlib.h"
#include <openssl/asn1t.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include <openssl/cms.h>
#include "cms_lcl.h"
#include "asn1_locl.h"
/* CMS SignedData Utilities */
DECLARE_ASN1_ITEM(CMS_SignedData)
static CMS_SignedData *cms_get0_signed(CMS_ContentInfo *cms)
{
if (OBJ_obj2nid(cms->contentType) != NID_pkcs7_signed)
{
CMSerr(CMS_F_CMS_GET0_SIGNED, CMS_R_CONTENT_TYPE_NOT_SIGNED_DATA);
return NULL;
}
return cms->d.signedData;
}
static CMS_SignedData *cms_signed_data_init(CMS_ContentInfo *cms)
{
if (cms->d.other == NULL)
{
cms->d.signedData = M_ASN1_new_of(CMS_SignedData);
if (!cms->d.signedData)
{
CMSerr(CMS_F_CMS_SIGNED_DATA_INIT, ERR_R_MALLOC_FAILURE);
return NULL;
}
cms->d.signedData->version = 1;
cms->d.signedData->encapContentInfo->eContentType =
OBJ_nid2obj(NID_pkcs7_data);
cms->d.signedData->encapContentInfo->partial = 1;
ASN1_OBJECT_free(cms->contentType);
cms->contentType = OBJ_nid2obj(NID_pkcs7_signed);
return cms->d.signedData;
}
return cms_get0_signed(cms);
}
/* Just initialize SignedData e.g. for certs only structure */
int CMS_SignedData_init(CMS_ContentInfo *cms)
{
if (cms_signed_data_init(cms))
return 1;
else
return 0;
}
/* Check structures and fixup version numbers (if necessary) */
static void cms_sd_set_version(CMS_SignedData *sd)
{
int i;
CMS_CertificateChoices *cch;
CMS_RevocationInfoChoice *rch;
CMS_SignerInfo *si;
for (i = 0; i < sk_CMS_CertificateChoices_num(sd->certificates); i++)
{
cch = sk_CMS_CertificateChoices_value(sd->certificates, i);
if (cch->type == CMS_CERTCHOICE_OTHER)
{
if (sd->version < 5)
sd->version = 5;
}
else if (cch->type == CMS_CERTCHOICE_V2ACERT)
{
if (sd->version < 4)
sd->version = 4;
}
else if (cch->type == CMS_CERTCHOICE_V1ACERT)
{
if (sd->version < 3)
sd->version = 3;
}
}
for (i = 0; i < sk_CMS_RevocationInfoChoice_num(sd->crls); i++)
{
rch = sk_CMS_RevocationInfoChoice_value(sd->crls, i);
if (rch->type == CMS_REVCHOICE_OTHER)
{
if (sd->version < 5)
sd->version = 5;
}
}
if ((OBJ_obj2nid(sd->encapContentInfo->eContentType) != NID_pkcs7_data)
&& (sd->version < 3))
sd->version = 3;
for (i = 0; i < sk_CMS_SignerInfo_num(sd->signerInfos); i++)
{
si = sk_CMS_SignerInfo_value(sd->signerInfos, i);
if (si->sid->type == CMS_SIGNERINFO_KEYIDENTIFIER)
{
if (si->version < 3)
si->version = 3;
if (sd->version < 3)
sd->version = 3;
}
else
sd->version = 1;
}
if (sd->version < 1)
sd->version = 1;
}
/* Copy an existing messageDigest value */
static int cms_copy_messageDigest(CMS_ContentInfo *cms, CMS_SignerInfo *si)
{
STACK_OF(CMS_SignerInfo) *sinfos;
CMS_SignerInfo *sitmp;
int i;
sinfos = CMS_get0_SignerInfos(cms);
for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++)
{
ASN1_OCTET_STRING *messageDigest;
sitmp = sk_CMS_SignerInfo_value(sinfos, i);
if (sitmp == si)
continue;
if (CMS_signed_get_attr_count(sitmp) < 0)
continue;
if (OBJ_cmp(si->digestAlgorithm->algorithm,
sitmp->digestAlgorithm->algorithm))
continue;
messageDigest = CMS_signed_get0_data_by_OBJ(sitmp,
OBJ_nid2obj(NID_pkcs9_messageDigest),
-3, V_ASN1_OCTET_STRING);
if (!messageDigest)
{
CMSerr(CMS_F_CMS_COPY_MESSAGEDIGEST,
CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE);
return 0;
}
if (CMS_signed_add1_attr_by_NID(si, NID_pkcs9_messageDigest,
V_ASN1_OCTET_STRING,
messageDigest, -1))
return 1;
else
return 0;
}
CMSerr(CMS_F_CMS_COPY_MESSAGEDIGEST, CMS_R_NO_MATCHING_DIGEST);
return 0;
}
int cms_set1_SignerIdentifier(CMS_SignerIdentifier *sid, X509 *cert, int type)
{
switch(type)
{
case CMS_SIGNERINFO_ISSUER_SERIAL:
if (!cms_set1_ias(&sid->d.issuerAndSerialNumber, cert))
return 0;
break;
case CMS_SIGNERINFO_KEYIDENTIFIER:
if (!cms_set1_keyid(&sid->d.subjectKeyIdentifier, cert))
return 0;
break;
default:
CMSerr(CMS_F_CMS_SET1_SIGNERIDENTIFIER, CMS_R_UNKNOWN_ID);
return 0;
}
sid->type = type;
return 1;
}
int cms_SignerIdentifier_get0_signer_id(CMS_SignerIdentifier *sid,
ASN1_OCTET_STRING **keyid,
X509_NAME **issuer, ASN1_INTEGER **sno)
{
if (sid->type == CMS_SIGNERINFO_ISSUER_SERIAL)
{
if (issuer)
*issuer = sid->d.issuerAndSerialNumber->issuer;
if (sno)
*sno = sid->d.issuerAndSerialNumber->serialNumber;
}
else if (sid->type == CMS_SIGNERINFO_KEYIDENTIFIER)
{
if (keyid)
*keyid = sid->d.subjectKeyIdentifier;
}
else
return 0;
return 1;
}
int cms_SignerIdentifier_cert_cmp(CMS_SignerIdentifier *sid, X509 *cert)
{
if (sid->type == CMS_SIGNERINFO_ISSUER_SERIAL)
return cms_ias_cert_cmp(sid->d.issuerAndSerialNumber, cert);
else if (sid->type == CMS_SIGNERINFO_KEYIDENTIFIER)
return cms_keyid_cert_cmp(sid->d.subjectKeyIdentifier, cert);
else
return -1;
}
static int cms_sd_asn1_ctrl(CMS_SignerInfo *si, int cmd)
{
EVP_PKEY *pkey = si->pkey;
int i;
if (!pkey->ameth || !pkey->ameth->pkey_ctrl)
return 1;
i = pkey->ameth->pkey_ctrl(pkey, ASN1_PKEY_CTRL_CMS_SIGN, cmd, si);
if (i == -2)
{
CMSerr(CMS_F_CMS_SD_ASN1_CTRL,
CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
return 0;
}
if (i <= 0)
{
CMSerr(CMS_F_CMS_SD_ASN1_CTRL, CMS_R_CTRL_FAILURE);
return 0;
}
return 1;
}
CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *cms,
X509 *signer, EVP_PKEY *pk, const EVP_MD *md,
unsigned int flags)
{
CMS_SignedData *sd;
CMS_SignerInfo *si = NULL;
X509_ALGOR *alg;
int i, type;
if(!X509_check_private_key(signer, pk))
{
CMSerr(CMS_F_CMS_ADD1_SIGNER,
CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
return NULL;
}
sd = cms_signed_data_init(cms);
if (!sd)
goto err;
si = M_ASN1_new_of(CMS_SignerInfo);
if (!si)
goto merr;
X509_check_purpose(signer, -1, -1);
CRYPTO_add(&pk->references, 1, CRYPTO_LOCK_EVP_PKEY);
CRYPTO_add(&signer->references, 1, CRYPTO_LOCK_X509);
si->pkey = pk;
si->signer = signer;
EVP_MD_CTX_init(&si->mctx);
si->pctx = NULL;
if (flags & CMS_USE_KEYID)
{
si->version = 3;
if (sd->version < 3)
sd->version = 3;
type = CMS_SIGNERINFO_KEYIDENTIFIER;
}
else
{
type = CMS_SIGNERINFO_ISSUER_SERIAL;
si->version = 1;
}
if (!cms_set1_SignerIdentifier(si->sid, signer, type))
goto err;
if (md == NULL)
{
int def_nid;
if (EVP_PKEY_get_default_digest_nid(pk, &def_nid) <= 0)
goto err;
md = EVP_get_digestbynid(def_nid);
if (md == NULL)
{
CMSerr(CMS_F_CMS_ADD1_SIGNER, CMS_R_NO_DEFAULT_DIGEST);
goto err;
}
}
if (!md)
{
CMSerr(CMS_F_CMS_ADD1_SIGNER, CMS_R_NO_DIGEST_SET);
goto err;
}
X509_ALGOR_set_md(si->digestAlgorithm, md);
/* See if digest is present in digestAlgorithms */
for (i = 0; i < sk_X509_ALGOR_num(sd->digestAlgorithms); i++)
{
ASN1_OBJECT *aoid;
alg = sk_X509_ALGOR_value(sd->digestAlgorithms, i);
X509_ALGOR_get0(&aoid, NULL, NULL, alg);
if (OBJ_obj2nid(aoid) == EVP_MD_type(md))
break;
}
if (i == sk_X509_ALGOR_num(sd->digestAlgorithms))
{
alg = X509_ALGOR_new();
if (!alg)
goto merr;
X509_ALGOR_set_md(alg, md);
if (!sk_X509_ALGOR_push(sd->digestAlgorithms, alg))
{
X509_ALGOR_free(alg);
goto merr;
}
}
if (!(flags & CMS_KEY_PARAM) && !cms_sd_asn1_ctrl(si, 0))
goto err;
if (!(flags & CMS_NOATTR))
{
/* Initialialize signed attributes strutucture so other
* attributes such as signing time etc are added later
* even if we add none here.
*/
if (!si->signedAttrs)
{
si->signedAttrs = sk_X509_ATTRIBUTE_new_null();
if (!si->signedAttrs)
goto merr;
}
if (!(flags & CMS_NOSMIMECAP))
{
STACK_OF(X509_ALGOR) *smcap = NULL;
i = CMS_add_standard_smimecap(&smcap);
if (i)
i = CMS_add_smimecap(si, smcap);
sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
if (!i)
goto merr;
}
if (flags & CMS_REUSE_DIGEST)
{
if (!cms_copy_messageDigest(cms, si))
goto err;
if (!(flags & (CMS_PARTIAL|CMS_KEY_PARAM)) &&
!CMS_SignerInfo_sign(si))
goto err;
}
}
if (!(flags & CMS_NOCERTS))
{
/* NB ignore -1 return for duplicate cert */
if (!CMS_add1_cert(cms, signer))
goto merr;
}
if (flags & CMS_KEY_PARAM)
{
if (flags & CMS_NOATTR)
{
si->pctx = EVP_PKEY_CTX_new(si->pkey, NULL);
if (!si->pctx)
goto err;
if (EVP_PKEY_sign_init(si->pctx) <= 0)
goto err;
if (EVP_PKEY_CTX_set_signature_md(si->pctx, md) <= 0)
goto err;
}
else if (EVP_DigestSignInit(&si->mctx, &si->pctx, md, NULL, pk) <= 0)
goto err;
}
if (!sd->signerInfos)
sd->signerInfos = sk_CMS_SignerInfo_new_null();
if (!sd->signerInfos ||
!sk_CMS_SignerInfo_push(sd->signerInfos, si))
goto merr;
return si;
merr:
CMSerr(CMS_F_CMS_ADD1_SIGNER, ERR_R_MALLOC_FAILURE);
err:
if (si)
M_ASN1_free_of(si, CMS_SignerInfo);
return NULL;
}
static int cms_add1_signingTime(CMS_SignerInfo *si, ASN1_TIME *t)
{
ASN1_TIME *tt;
int r = 0;
if (t)
tt = t;
else
tt = X509_gmtime_adj(NULL, 0);
if (!tt)
goto merr;
if (CMS_signed_add1_attr_by_NID(si, NID_pkcs9_signingTime,
tt->type, tt, -1) <= 0)
goto merr;
r = 1;
merr:
if (!t)
ASN1_TIME_free(tt);
if (!r)
CMSerr(CMS_F_CMS_ADD1_SIGNINGTIME, ERR_R_MALLOC_FAILURE);
return r;
}
EVP_PKEY_CTX *CMS_SignerInfo_get0_pkey_ctx(CMS_SignerInfo *si)
{
return si->pctx;
}
EVP_MD_CTX *CMS_SignerInfo_get0_md_ctx(CMS_SignerInfo *si)
{
return &si->mctx;
}
STACK_OF(CMS_SignerInfo) *CMS_get0_SignerInfos(CMS_ContentInfo *cms)
{
CMS_SignedData *sd;
sd = cms_get0_signed(cms);
if (!sd)
return NULL;
return sd->signerInfos;
}
STACK_OF(X509) *CMS_get0_signers(CMS_ContentInfo *cms)
{
STACK_OF(X509) *signers = NULL;
STACK_OF(CMS_SignerInfo) *sinfos;
CMS_SignerInfo *si;
int i;
sinfos = CMS_get0_SignerInfos(cms);
for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++)
{
si = sk_CMS_SignerInfo_value(sinfos, i);
if (si->signer)
{
if (!signers)
{
signers = sk_X509_new_null();
if (!signers)
return NULL;
}
if (!sk_X509_push(signers, si->signer))
{
sk_X509_free(signers);
return NULL;
}
}
}
return signers;
}
void CMS_SignerInfo_set1_signer_cert(CMS_SignerInfo *si, X509 *signer)
{
if (signer)
{
CRYPTO_add(&signer->references, 1, CRYPTO_LOCK_X509);
if (si->pkey)
EVP_PKEY_free(si->pkey);
si->pkey = X509_get_pubkey(signer);
}
if (si->signer)
X509_free(si->signer);
si->signer = signer;
}
int CMS_SignerInfo_get0_signer_id(CMS_SignerInfo *si,
ASN1_OCTET_STRING **keyid,
X509_NAME **issuer, ASN1_INTEGER **sno)
{
return cms_SignerIdentifier_get0_signer_id(si->sid, keyid, issuer, sno);
}
int CMS_SignerInfo_cert_cmp(CMS_SignerInfo *si, X509 *cert)
{
return cms_SignerIdentifier_cert_cmp(si->sid, cert);
}
int CMS_set1_signers_certs(CMS_ContentInfo *cms, STACK_OF(X509) *scerts,
unsigned int flags)
{
CMS_SignedData *sd;
CMS_SignerInfo *si;
CMS_CertificateChoices *cch;
STACK_OF(CMS_CertificateChoices) *certs;
X509 *x;
int i, j;
int ret = 0;
sd = cms_get0_signed(cms);
if (!sd)
return -1;
certs = sd->certificates;
for (i = 0; i < sk_CMS_SignerInfo_num(sd->signerInfos); i++)
{
si = sk_CMS_SignerInfo_value(sd->signerInfos, i);
if (si->signer)
continue;
for (j = 0; j < sk_X509_num(scerts); j++)
{
x = sk_X509_value(scerts, j);
if (CMS_SignerInfo_cert_cmp(si, x) == 0)
{
CMS_SignerInfo_set1_signer_cert(si, x);
ret++;
break;
}
}
if (si->signer || (flags & CMS_NOINTERN))
continue;
for (j = 0; j < sk_CMS_CertificateChoices_num(certs); j++)
{
cch = sk_CMS_CertificateChoices_value(certs, j);
if (cch->type != 0)
continue;
x = cch->d.certificate;
if (CMS_SignerInfo_cert_cmp(si, x) == 0)
{
CMS_SignerInfo_set1_signer_cert(si, x);
ret++;
break;
}
}
}
return ret;
}
void CMS_SignerInfo_get0_algs(CMS_SignerInfo *si, EVP_PKEY **pk, X509 **signer,
X509_ALGOR **pdig, X509_ALGOR **psig)
{
if (pk)
*pk = si->pkey;
if (signer)
*signer = si->signer;
if (pdig)
*pdig = si->digestAlgorithm;
if (psig)
*psig = si->signatureAlgorithm;
}
static int cms_SignerInfo_content_sign(CMS_ContentInfo *cms,
CMS_SignerInfo *si, BIO *chain)
{
EVP_MD_CTX mctx;
int r = 0;
EVP_PKEY_CTX *pctx = NULL;
EVP_MD_CTX_init(&mctx);
if (!si->pkey)
{
CMSerr(CMS_F_CMS_SIGNERINFO_CONTENT_SIGN, CMS_R_NO_PRIVATE_KEY);
return 0;
}
if (!cms_DigestAlgorithm_find_ctx(&mctx, chain, si->digestAlgorithm))
goto err;
/* Set SignerInfo algortihm details if we used custom parametsr */
if (si->pctx && !cms_sd_asn1_ctrl(si, 0))
goto err;
/* If any signed attributes calculate and add messageDigest attribute */
if (CMS_signed_get_attr_count(si) >= 0)
{
ASN1_OBJECT *ctype =
cms->d.signedData->encapContentInfo->eContentType;
unsigned char md[EVP_MAX_MD_SIZE];
unsigned int mdlen;
if (!EVP_DigestFinal_ex(&mctx, md, &mdlen))
goto err;
if (!CMS_signed_add1_attr_by_NID(si, NID_pkcs9_messageDigest,
V_ASN1_OCTET_STRING,
md, mdlen))
goto err;
/* Copy content type across */
if (CMS_signed_add1_attr_by_NID(si, NID_pkcs9_contentType,
V_ASN1_OBJECT, ctype, -1) <= 0)
goto err;
if (!CMS_SignerInfo_sign(si))
goto err;
}
else if (si->pctx)
{
unsigned char *sig;
size_t siglen;
unsigned char md[EVP_MAX_MD_SIZE];
unsigned int mdlen;
pctx = si->pctx;
if (!EVP_DigestFinal_ex(&mctx, md, &mdlen))
goto err;
siglen = EVP_PKEY_size(si->pkey);
sig = OPENSSL_malloc(siglen);
if (!sig)
{
CMSerr(CMS_F_CMS_SIGNERINFO_CONTENT_SIGN,
ERR_R_MALLOC_FAILURE);
goto err;
}
if (EVP_PKEY_sign(pctx, sig, &siglen, md, mdlen) <= 0)
goto err;
ASN1_STRING_set0(si->signature, sig, siglen);
}
else
{
unsigned char *sig;
unsigned int siglen;
sig = OPENSSL_malloc(EVP_PKEY_size(si->pkey));
if (!sig)
{
CMSerr(CMS_F_CMS_SIGNERINFO_CONTENT_SIGN,
ERR_R_MALLOC_FAILURE);
goto err;
}
if (!EVP_SignFinal(&mctx, sig, &siglen, si->pkey))
{
CMSerr(CMS_F_CMS_SIGNERINFO_CONTENT_SIGN,
CMS_R_SIGNFINAL_ERROR);
OPENSSL_free(sig);
goto err;
}
ASN1_STRING_set0(si->signature, sig, siglen);
}
r = 1;
err:
EVP_MD_CTX_cleanup(&mctx);
if (pctx)
EVP_PKEY_CTX_free(pctx);
return r;
}
int cms_SignedData_final(CMS_ContentInfo *cms, BIO *chain)
{
STACK_OF(CMS_SignerInfo) *sinfos;
CMS_SignerInfo *si;
int i;
sinfos = CMS_get0_SignerInfos(cms);
for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++)
{
si = sk_CMS_SignerInfo_value(sinfos, i);
if (!cms_SignerInfo_content_sign(cms, si, chain))
return 0;
}
cms->d.signedData->encapContentInfo->partial = 0;
return 1;
}
int CMS_SignerInfo_sign(CMS_SignerInfo *si)
{
EVP_MD_CTX *mctx = &si->mctx;
EVP_PKEY_CTX *pctx;
unsigned char *abuf = NULL;
int alen;
size_t siglen;
const EVP_MD *md = NULL;
md = EVP_get_digestbyobj(si->digestAlgorithm->algorithm);
if (md == NULL)
return 0;
if (CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1) < 0)
{
if (!cms_add1_signingTime(si, NULL))
goto err;
}
if (si->pctx)
pctx = si->pctx;
else
{
EVP_MD_CTX_init(mctx);
if (EVP_DigestSignInit(mctx, &pctx, md, NULL, si->pkey) <= 0)
goto err;
}
if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_SIGN,
EVP_PKEY_CTRL_CMS_SIGN, 0, si) <= 0)
{
CMSerr(CMS_F_CMS_SIGNERINFO_SIGN, CMS_R_CTRL_ERROR);
goto err;
}
alen = ASN1_item_i2d((ASN1_VALUE *)si->signedAttrs,&abuf,
ASN1_ITEM_rptr(CMS_Attributes_Sign));
if(!abuf)
goto err;
if (EVP_DigestSignUpdate(mctx, abuf, alen) <= 0)
goto err;
if (EVP_DigestSignFinal(mctx, NULL, &siglen) <= 0)
goto err;
OPENSSL_free(abuf);
abuf = OPENSSL_malloc(siglen);
if(!abuf)
goto err;
if (EVP_DigestSignFinal(mctx, abuf, &siglen) <= 0)
goto err;
if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_SIGN,
EVP_PKEY_CTRL_CMS_SIGN, 1, si) <= 0)
{
CMSerr(CMS_F_CMS_SIGNERINFO_SIGN, CMS_R_CTRL_ERROR);
goto err;
}
EVP_MD_CTX_cleanup(mctx);
ASN1_STRING_set0(si->signature, abuf, siglen);
return 1;
err:
if (abuf)
OPENSSL_free(abuf);
EVP_MD_CTX_cleanup(mctx);
return 0;
}
int CMS_SignerInfo_verify(CMS_SignerInfo *si)
{
EVP_MD_CTX *mctx = &si->mctx;
unsigned char *abuf = NULL;
int alen, r = -1;
const EVP_MD *md = NULL;
if (!si->pkey)
{
CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY, CMS_R_NO_PUBLIC_KEY);
return -1;
}
md = EVP_get_digestbyobj(si->digestAlgorithm->algorithm);
if (md == NULL)
return -1;
EVP_MD_CTX_init(mctx);
if (EVP_DigestVerifyInit(mctx, &si->pctx, md, NULL, si->pkey) <= 0)
goto err;
if (!cms_sd_asn1_ctrl(si, 1))
goto err;
alen = ASN1_item_i2d((ASN1_VALUE *)si->signedAttrs,&abuf,
ASN1_ITEM_rptr(CMS_Attributes_Verify));
if(!abuf)
goto err;
r = EVP_DigestVerifyUpdate(mctx, abuf, alen);
OPENSSL_free(abuf);
if (r <= 0)
{
r = -1;
goto err;
}
r = EVP_DigestVerifyFinal(mctx,
si->signature->data, si->signature->length);
if (r <= 0)
CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY, CMS_R_VERIFICATION_FAILURE);
err:
EVP_MD_CTX_cleanup(mctx);
return r;
}
/* Create a chain of digest BIOs from a CMS ContentInfo */
BIO *cms_SignedData_init_bio(CMS_ContentInfo *cms)
{
int i;
CMS_SignedData *sd;
BIO *chain = NULL;
sd = cms_get0_signed(cms);
if (!sd)
return NULL;
if (cms->d.signedData->encapContentInfo->partial)
cms_sd_set_version(sd);
for (i = 0; i < sk_X509_ALGOR_num(sd->digestAlgorithms); i++)
{
X509_ALGOR *digestAlgorithm;
BIO *mdbio;
digestAlgorithm = sk_X509_ALGOR_value(sd->digestAlgorithms, i);
mdbio = cms_DigestAlgorithm_init_bio(digestAlgorithm);
if (!mdbio)
goto err;
if (chain)
BIO_push(chain, mdbio);
else
chain = mdbio;
}
return chain;
err:
if (chain)
BIO_free_all(chain);
return NULL;
}
int CMS_SignerInfo_verify_content(CMS_SignerInfo *si, BIO *chain)
{
ASN1_OCTET_STRING *os = NULL;
EVP_MD_CTX mctx;
EVP_PKEY_CTX *pkctx = NULL;
int r = -1;
unsigned char mval[EVP_MAX_MD_SIZE];
unsigned int mlen;
EVP_MD_CTX_init(&mctx);
/* If we have any signed attributes look for messageDigest value */
if (CMS_signed_get_attr_count(si) >= 0)
{
os = CMS_signed_get0_data_by_OBJ(si,
OBJ_nid2obj(NID_pkcs9_messageDigest),
-3, V_ASN1_OCTET_STRING);
if (!os)
{
CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT,
CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE);
goto err;
}
}
if (!cms_DigestAlgorithm_find_ctx(&mctx, chain, si->digestAlgorithm))
goto err;
if (EVP_DigestFinal_ex(&mctx, mval, &mlen) <= 0)
{
CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT,
CMS_R_UNABLE_TO_FINALIZE_CONTEXT);
goto err;
}
/* If messageDigest found compare it */
if (os)
{
if (mlen != (unsigned int)os->length)
{
CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT,
CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH);
goto err;
}
if (memcmp(mval, os->data, mlen))
{
CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT,
CMS_R_VERIFICATION_FAILURE);
r = 0;
}
else
r = 1;
}
else
{
const EVP_MD *md = EVP_MD_CTX_md(&mctx);
pkctx = EVP_PKEY_CTX_new(si->pkey, NULL);
if (EVP_PKEY_verify_init(pkctx) <= 0)
goto err;
if (EVP_PKEY_CTX_set_signature_md(pkctx, md) <= 0)
goto err;
si->pctx = pkctx;
if (!cms_sd_asn1_ctrl(si, 1))
goto err;
r = EVP_PKEY_verify(pkctx, si->signature->data,
si->signature->length,
mval, mlen);
if (r <= 0)
{
CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT,
CMS_R_VERIFICATION_FAILURE);
r = 0;
}
}
err:
if (pkctx)
EVP_PKEY_CTX_free(pkctx);
EVP_MD_CTX_cleanup(&mctx);
return r;
}
int CMS_add_smimecap(CMS_SignerInfo *si, STACK_OF(X509_ALGOR) *algs)
{
unsigned char *smder = NULL;
int smderlen, r;
smderlen = i2d_X509_ALGORS(algs, &smder);
if (smderlen <= 0)
return 0;
r = CMS_signed_add1_attr_by_NID(si, NID_SMIMECapabilities,
V_ASN1_SEQUENCE, smder, smderlen);
OPENSSL_free(smder);
return r;
}
int CMS_add_simple_smimecap(STACK_OF(X509_ALGOR) **algs,
int algnid, int keysize)
{
X509_ALGOR *alg;
ASN1_INTEGER *key = NULL;
if (keysize > 0)
{
key = ASN1_INTEGER_new();
if (!key || !ASN1_INTEGER_set(key, keysize))
return 0;
}
alg = X509_ALGOR_new();
if (!alg)
{
if (key)
ASN1_INTEGER_free(key);
return 0;
}
X509_ALGOR_set0(alg, OBJ_nid2obj(algnid),
key ? V_ASN1_INTEGER : V_ASN1_UNDEF, key);
if (!*algs)
*algs = sk_X509_ALGOR_new_null();
if (!*algs || !sk_X509_ALGOR_push(*algs, alg))
{
X509_ALGOR_free(alg);
return 0;
}
return 1;
}
/* Check to see if a cipher exists and if so add S/MIME capabilities */
static int cms_add_cipher_smcap(STACK_OF(X509_ALGOR) **sk, int nid, int arg)
{
if (EVP_get_cipherbynid(nid))
return CMS_add_simple_smimecap(sk, nid, arg);
return 1;
}
static int cms_add_digest_smcap(STACK_OF(X509_ALGOR) **sk, int nid, int arg)
{
if (EVP_get_digestbynid(nid))
return CMS_add_simple_smimecap(sk, nid, arg);
return 1;
}
int CMS_add_standard_smimecap(STACK_OF(X509_ALGOR) **smcap)
{
if (!cms_add_cipher_smcap(smcap, NID_aes_256_cbc, -1)
|| !cms_add_digest_smcap(smcap, NID_id_GostR3411_94, -1)
|| !cms_add_cipher_smcap(smcap, NID_id_Gost28147_89, -1)
|| !cms_add_cipher_smcap(smcap, NID_aes_192_cbc, -1)
|| !cms_add_cipher_smcap(smcap, NID_aes_128_cbc, -1)
|| !cms_add_cipher_smcap(smcap, NID_des_ede3_cbc, -1)
|| !cms_add_cipher_smcap(smcap, NID_rc2_cbc, 128)
|| !cms_add_cipher_smcap(smcap, NID_rc2_cbc, 64)
|| !cms_add_cipher_smcap(smcap, NID_des_cbc, -1)
|| !cms_add_cipher_smcap(smcap, NID_rc2_cbc, 40))
return 0;
return 1;
}