You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
902 lines
26 KiB
902 lines
26 KiB
/* 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/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 if (si->version < 1) |
|
si->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: |
|
sid->d.issuerAndSerialNumber = |
|
M_ASN1_new_of(CMS_IssuerAndSerialNumber); |
|
if (!sid->d.issuerAndSerialNumber) |
|
goto merr; |
|
if (!X509_NAME_set(&sid->d.issuerAndSerialNumber->issuer, |
|
X509_get_issuer_name(cert))) |
|
goto merr; |
|
if (!ASN1_STRING_copy(sid->d.issuerAndSerialNumber->serialNumber, |
|
X509_get_serialNumber(cert))) |
|
goto merr; |
|
break; |
|
|
|
case CMS_SIGNERINFO_KEYIDENTIFIER: |
|
if (!cert->skid) { |
|
CMSerr(CMS_F_CMS_SET1_SIGNERIDENTIFIER, |
|
CMS_R_CERTIFICATE_HAS_NO_KEYID); |
|
return 0; |
|
} |
|
sid->d.subjectKeyIdentifier = ASN1_STRING_dup(cert->skid); |
|
if (!sid->d.subjectKeyIdentifier) |
|
goto merr; |
|
break; |
|
|
|
default: |
|
CMSerr(CMS_F_CMS_SET1_SIGNERIDENTIFIER, CMS_R_UNKNOWN_ID); |
|
return 0; |
|
} |
|
|
|
sid->type = type; |
|
|
|
return 1; |
|
|
|
merr: |
|
CMSerr(CMS_F_CMS_SET1_SIGNERIDENTIFIER, ERR_R_MALLOC_FAILURE); |
|
return 0; |
|
|
|
} |
|
|
|
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) |
|
{ |
|
int ret; |
|
if (sid->type == CMS_SIGNERINFO_ISSUER_SERIAL) { |
|
ret = X509_NAME_cmp(sid->d.issuerAndSerialNumber->issuer, |
|
X509_get_issuer_name(cert)); |
|
if (ret) |
|
return ret; |
|
return ASN1_INTEGER_cmp(sid->d.issuerAndSerialNumber->serialNumber, |
|
X509_get_serialNumber(cert)); |
|
} else if (sid->type == CMS_SIGNERINFO_KEYIDENTIFIER) { |
|
X509_check_purpose(cert, -1, -1); |
|
if (!cert->skid) |
|
return -1; |
|
return ASN1_OCTET_STRING_cmp(sid->d.subjectKeyIdentifier, cert->skid); |
|
} else |
|
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; |
|
|
|
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; |
|
} |
|
|
|
cms_DigestAlgorithm_set(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; |
|
cms_DigestAlgorithm_set(alg, md); |
|
if (!sk_X509_ALGOR_push(sd->digestAlgorithms, alg)) { |
|
X509_ALGOR_free(alg); |
|
goto merr; |
|
} |
|
} |
|
|
|
if (pk->ameth && pk->ameth->pkey_ctrl) { |
|
i = pk->ameth->pkey_ctrl(pk, ASN1_PKEY_CTRL_CMS_SIGN, 0, si); |
|
if (i == -2) { |
|
CMSerr(CMS_F_CMS_ADD1_SIGNER, |
|
CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE); |
|
goto err; |
|
} |
|
if (i <= 0) { |
|
CMSerr(CMS_F_CMS_ADD1_SIGNER, CMS_R_CTRL_FAILURE); |
|
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_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 (!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; |
|
|
|
} |
|
|
|
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_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; |
|
|
|
/* |
|
* 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 { |
|
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); |
|
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; |
|
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; |
|
|
|
EVP_MD_CTX_init(&mctx); |
|
|
|
if (CMS_signed_get_attr_by_NID(si, NID_pkcs9_signingTime, -1) < 0) { |
|
if (!cms_add1_signingTime(si, NULL)) |
|
goto err; |
|
} |
|
|
|
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; |
|
EVP_PKEY_CTX *pctx; |
|
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, &pctx, md, NULL, si->pkey) <= 0) |
|
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; |
|
int r = -1; |
|
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 messageDigest found compare it */ |
|
|
|
if (os) { |
|
unsigned char mval[EVP_MAX_MD_SIZE]; |
|
unsigned int mlen; |
|
if (EVP_DigestFinal_ex(&mctx, mval, &mlen) <= 0) { |
|
CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT, |
|
CMS_R_UNABLE_TO_FINALIZE_CONTEXT); |
|
goto err; |
|
} |
|
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 { |
|
r = EVP_VerifyFinal(&mctx, si->signature->data, |
|
si->signature->length, si->pkey); |
|
if (r <= 0) { |
|
CMSerr(CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT, |
|
CMS_R_VERIFICATION_FAILURE); |
|
r = 0; |
|
} |
|
} |
|
|
|
err: |
|
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; |
|
}
|
|
|