12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- /*
- * CMS signerInfo methods.
- */
- #include "cmslocal.h"
- #include "cert.h"
- #include "keyhi.h"
- #include "secasn1.h"
- #include "secitem.h"
- #include "secoid.h"
- #include "pk11func.h"
- #include "prtime.h"
- #include "secerr.h"
- #include "secder.h"
- #include "cryptohi.h"
- #include "smime.h"
- /* =============================================================================
- * SIGNERINFO
- */
- NSSCMSSignerInfo *
- nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type,
- CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey,
- SECKEYPrivateKey *signingKey, SECOidTag digestalgtag);
- NSSCMSSignerInfo *
- NSS_CMSSignerInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, SECItem *subjKeyID,
- SECKEYPublicKey *pubKey,
- SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
- {
- return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_SubjectKeyID, NULL,
- subjKeyID, pubKey, signingKey, digestalgtag);
- }
- NSSCMSSignerInfo *
- NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag)
- {
- return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_IssuerSN, cert, NULL,
- NULL, NULL, digestalgtag);
- }
- NSSCMSSignerInfo *
- nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type,
- CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey,
- SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
- {
- void *mark;
- NSSCMSSignerInfo *signerinfo;
- int version;
- PLArenaPool *poolp;
- SECStatus rv;
- poolp = cmsg->poolp;
- mark = PORT_ArenaMark(poolp);
- signerinfo = (NSSCMSSignerInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignerInfo));
- if (signerinfo == NULL) {
- PORT_ArenaRelease(poolp, mark);
- return NULL;
- }
- signerinfo->cmsg = cmsg;
- switch (type) {
- case NSSCMSSignerID_IssuerSN:
- signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_IssuerSN;
- if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
- goto loser;
- if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
- goto loser;
- break;
- case NSSCMSSignerID_SubjectKeyID:
- signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_SubjectKeyID;
- PORT_Assert(subjKeyID);
- if (!subjKeyID)
- goto loser;
- signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SECItem);
- rv = SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
- subjKeyID);
- if (rv != SECSuccess) {
- goto loser;
- }
- signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
- if (!signerinfo->signingKey)
- goto loser;
- signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
- if (!signerinfo->pubKey)
- goto loser;
- break;
- default:
- goto loser;
- }
- /* set version right now */
- version = NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN;
- /* RFC2630 5.3 "version is the syntax version number. If the .... " */
- if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID)
- version = NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY;
- (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
- if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
- goto loser;
- PORT_ArenaUnmark(poolp, mark);
- return signerinfo;
- loser:
- PORT_ArenaRelease(poolp, mark);
- return NULL;
- }
- /*
- * NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure
- */
- void
- NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si)
- {
- if (si->cert != NULL)
- CERT_DestroyCertificate(si->cert);
- if (si->certList != NULL)
- CERT_DestroyCertificateList(si->certList);
- /* XXX storage ??? */
- }
- static SECOidTag
- NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(KeyType keyType,
- SECOidTag pubkAlgTag,
- SECOidTag signAlgTag)
- {
- switch (keyType) {
- case rsaKey:
- return pubkAlgTag;
- case rsaPssKey:
- case dsaKey:
- case ecKey:
- return signAlgTag;
- default:
- return SEC_OID_UNKNOWN;
- }
- }
- /*
- * NSS_CMSSignerInfo_Sign - sign something
- *
- */
- SECStatus
- NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest,
- SECItem *contentType)
- {
- CERTCertificate *cert;
- SECKEYPrivateKey *privkey = NULL;
- SECOidTag digestalgtag;
- SECOidTag pubkAlgTag;
- SECOidTag signAlgTag;
- SECOidTag cmsSignAlgTag;
- SECItem signature = { 0 };
- SECStatus rv;
- PLArenaPool *poolp, *tmppoolp = NULL;
- SECAlgorithmID *algID, freeAlgID;
- CERTSubjectPublicKeyInfo *spki;
- PORT_Assert(digest != NULL);
- poolp = signerinfo->cmsg->poolp;
- switch (signerinfo->signerIdentifier.identifierType) {
- case NSSCMSSignerID_IssuerSN:
- cert = signerinfo->cert;
- privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg);
- if (privkey == NULL)
- goto loser;
- algID = &cert->subjectPublicKeyInfo.algorithm;
- break;
- case NSSCMSSignerID_SubjectKeyID:
- privkey = signerinfo->signingKey;
- signerinfo->signingKey = NULL;
- spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
- SECKEY_DestroyPublicKey(signerinfo->pubKey);
- signerinfo->pubKey = NULL;
- SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
- SECKEY_DestroySubjectPublicKeyInfo(spki);
- algID = &freeAlgID;
- break;
- default:
- goto loser;
- }
- digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
- /*
- * XXX I think there should be a cert-level interface for this,
- * so that I do not have to know about subjectPublicKeyInfo...
- */
- pubkAlgTag = SECOID_GetAlgorithmTag(algID);
- if (algID == &freeAlgID) {
- SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
- }
- signAlgTag = SEC_GetSignatureAlgorithmOidTag(SECKEY_GetPrivateKeyType(privkey),
- digestalgtag);
- if (signAlgTag == SEC_OID_UNKNOWN) {
- PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
- goto loser;
- }
- cmsSignAlgTag = NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(
- SECKEY_GetPrivateKeyType(privkey), pubkAlgTag, signAlgTag);
- if (cmsSignAlgTag == SEC_OID_UNKNOWN) {
- PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
- goto loser;
- }
- if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg),
- cmsSignAlgTag, NULL) != SECSuccess)
- goto loser;
- if (signerinfo->authAttr != NULL) {
- SECItem encoded_attrs;
- /* find and fill in the message digest attribute. */
- rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr),
- SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
- if (rv != SECSuccess)
- goto loser;
- if (contentType != NULL) {
- /* if the caller wants us to, find and fill in the content type attribute. */
- rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr),
- SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
- if (rv != SECSuccess)
- goto loser;
- }
- if ((tmppoolp = PORT_NewArena(1024)) == NULL) {
- PORT_SetError(SEC_ERROR_NO_MEMORY);
- goto loser;
- }
- /*
- * Before encoding, reorder the attributes so that when they
- * are encoded, they will be conforming DER, which is required
- * to have a specific order and that is what must be used for
- * the hash/signature. We do this here, rather than building
- * it into EncodeAttributes, because we do not want to do
- * such reordering on incoming messages (which also uses
- * EncodeAttributes) or our old signatures (and other "broken"
- * implementations) will not verify. So, we want to guarantee
- * that we send out good DER encodings of attributes, but not
- * to expect to receive them.
- */
- if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess)
- goto loser;
- encoded_attrs.data = NULL;
- encoded_attrs.len = 0;
- if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr),
- &encoded_attrs) == NULL)
- goto loser;
- rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len,
- privkey, signAlgTag);
- PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
- tmppoolp = 0;
- } else {
- rv = SGN_Digest(privkey, digestalgtag, &signature, digest);
- }
- SECKEY_DestroyPrivateKey(privkey);
- privkey = NULL;
- if (rv != SECSuccess)
- goto loser;
- if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) != SECSuccess)
- goto loser;
- SECITEM_FreeItem(&signature, PR_FALSE);
- return SECSuccess;
- loser:
- if (signature.len != 0)
- SECITEM_FreeItem(&signature, PR_FALSE);
- if (privkey)
- SECKEY_DestroyPrivateKey(privkey);
- if (tmppoolp)
- PORT_FreeArena(tmppoolp, PR_FALSE);
- return SECFailure;
- }
- SECStatus
- NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb,
- SECCertUsage certusage)
- {
- CERTCertificate *cert;
- PRTime stime;
- if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) {
- signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
- return SECFailure;
- }
- /*
- * Get and convert the signing time; if available, it will be used
- * both on the cert verification and for importing the sender
- * email profile.
- */
- if (NSS_CMSSignerInfo_GetSigningTime(signerinfo, &stime) != SECSuccess)
- stime = PR_Now(); /* not found or conversion failed, so check against now */
- /*
- * XXX This uses the signing time, if available. Additionally, we
- * might want to, if there is no signing time, get the message time
- * from the mail header itself, and use that. That would require
- * a change to our interface though, and for S/MIME callers to pass
- * in a time (and for non-S/MIME callers to pass in nothing, or
- * maybe make them pass in the current time, always?).
- */
- if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime,
- signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
- signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted;
- return SECFailure;
- }
- return SECSuccess;
- }
- /*
- * NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo
- *
- * Just verifies the signature. The assumption is that verification of
- * the certificate is done already.
- */
- SECStatus
- NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo,
- SECItem *digest, /* may be NULL */
- SECItem *contentType) /* may be NULL */
- {
- SECKEYPublicKey *publickey = NULL;
- NSSCMSAttribute *attr;
- SECItem encoded_attrs;
- CERTCertificate *cert;
- NSSCMSVerificationStatus vs = NSSCMSVS_Unverified;
- PLArenaPool *poolp;
- SECOidTag digestalgtag;
- SECOidTag pubkAlgTag;
- SECOidTag digestalgtagCmp;
- SECOidTag sigAlgTag;
- if (signerinfo == NULL)
- return SECFailure;
- /* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL
- ** and cert has not been verified
- */
- cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL);
- if (cert == NULL) {
- vs = NSSCMSVS_SigningCertNotFound;
- goto loser;
- }
- if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) {
- vs = NSSCMSVS_ProcessingError;
- goto loser;
- }
- digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
- pubkAlgTag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
- sigAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
- if ((pubkAlgTag == SEC_OID_UNKNOWN) || (digestalgtag == SEC_OID_UNKNOWN) ||
- (sigAlgTag == SEC_OID_UNKNOWN)) {
- vs = NSSCMSVS_SignatureAlgorithmUnknown;
- goto loser;
- }
- if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
- if (contentType) {
- /*
- * Check content type
- *
- * RFC2630 sez that if there are any authenticated attributes,
- * then there must be one for content type which matches the
- * content type of the content being signed, and there must
- * be one for message digest which matches our message digest.
- * So check these things first.
- */
- attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
- SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
- if (attr == NULL) {
- vs = NSSCMSVS_MalformedSignature;
- goto loser;
- }
- if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) {
- vs = NSSCMSVS_MalformedSignature;
- goto loser;
- }
- }
- /*
- * Check digest
- */
- attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
- SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
- if (attr == NULL) {
- vs = NSSCMSVS_MalformedSignature;
- goto loser;
- }
- if (!digest ||
- NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) {
- vs = NSSCMSVS_DigestMismatch;
- goto loser;
- }
- if ((poolp = PORT_NewArena(1024)) == NULL) {
- vs = NSSCMSVS_ProcessingError;
- goto loser;
- }
- /*
- * Check signature
- *
- * The signature is based on a digest of the DER-encoded authenticated
- * attributes. So, first we encode and then we digest/verify.
- * we trust the decoder to have the attributes in the right (sorted)
- * order
- */
- encoded_attrs.data = NULL;
- encoded_attrs.len = 0;
- if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr),
- &encoded_attrs) == NULL ||
- encoded_attrs.data == NULL || encoded_attrs.len == 0) {
- PORT_FreeArena(poolp, PR_FALSE);
- vs = NSSCMSVS_ProcessingError;
- goto loser;
- }
- if (sigAlgTag == pubkAlgTag) {
- /* This is to handle cases in which signatureAlgorithm field
- * specifies the public key algorithm rather than a signature
- * algorithm. */
- vs = (VFY_VerifyDataDirect(encoded_attrs.data, encoded_attrs.len,
- publickey, &(signerinfo->encDigest), pubkAlgTag,
- digestalgtag, NULL, signerinfo->cmsg->pwfn_arg) != SECSuccess)
- ? NSSCMSVS_BadSignature
- : NSSCMSVS_GoodSignature;
- } else {
- if (VFY_VerifyDataWithAlgorithmID(encoded_attrs.data,
- encoded_attrs.len, publickey, &(signerinfo->encDigest),
- &(signerinfo->digestEncAlg), &digestalgtagCmp,
- signerinfo->cmsg->pwfn_arg) != SECSuccess) {
- vs = NSSCMSVS_BadSignature;
- } else if (digestalgtagCmp != digestalgtag) {
- PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
- vs = NSSCMSVS_BadSignature;
- } else {
- vs = NSSCMSVS_GoodSignature;
- }
- }
- PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
- } else {
- SECItem *sig;
- /* No authenticated attributes.
- ** The signature is based on the plain message digest.
- */
- sig = &(signerinfo->encDigest);
- if (sig->len == 0)
- goto loser;
- if (sigAlgTag == pubkAlgTag) {
- /* This is to handle cases in which signatureAlgorithm field
- * specifies the public key algorithm rather than a signature
- * algorithm. */
- vs = (!digest ||
- VFY_VerifyDigestDirect(digest, publickey, sig, pubkAlgTag,
- digestalgtag, signerinfo->cmsg->pwfn_arg) != SECSuccess)
- ? NSSCMSVS_BadSignature
- : NSSCMSVS_GoodSignature;
- } else {
- vs = (!digest ||
- VFY_VerifyDigestWithAlgorithmID(digest, publickey, sig,
- &(signerinfo->digestEncAlg), digestalgtag,
- signerinfo->cmsg->pwfn_arg) != SECSuccess)
- ? NSSCMSVS_BadSignature
- : NSSCMSVS_GoodSignature;
- }
- }
- if (vs == NSSCMSVS_BadSignature) {
- int error = PORT_GetError();
- /*
- * XXX Change the generic error into our specific one, because
- * in that case we get a better explanation out of the Security
- * Advisor. This is really a bug in the PSM error strings (the
- * "generic" error has a lousy/wrong message associated with it
- * which assumes the signature verification was done for the
- * purposes of checking the issuer signature on a certificate)
- * but this is at least an easy workaround and/or in the
- * Security Advisor, which specifically checks for the error
- * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
- * in that case but does not similarly check for
- * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
- * probably say the wrong thing in the case that it *was* the
- * certificate signature check that failed during the cert
- * verification done above. Our error handling is really a mess.
- */
- if (error == SEC_ERROR_BAD_SIGNATURE)
- PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
- /*
- * map algorithm failures to NSSCMSVS values
- */
- if ((error == SEC_ERROR_PKCS7_KEYALG_MISMATCH) ||
- (error == SEC_ERROR_INVALID_ALGORITHM)) {
- /* keep the same error code as 3.11 and before */
- PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
- vs = NSSCMSVS_SignatureAlgorithmUnsupported;
- }
- }
- if (publickey != NULL)
- SECKEY_DestroyPublicKey(publickey);
- signerinfo->verificationStatus = vs;
- return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure;
- loser:
- if (publickey != NULL)
- SECKEY_DestroyPublicKey(publickey);
- signerinfo->verificationStatus = vs;
- PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
- return SECFailure;
- }
- NSSCMSVerificationStatus
- NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo)
- {
- return signerinfo->verificationStatus;
- }
- SECOidData *
- NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo)
- {
- SECOidData *algdata;
- SECOidTag algtag;
- algdata = SECOID_FindOID(&(signerinfo->digestAlg.algorithm));
- if (algdata == NULL) {
- return algdata;
- }
- /* Windows may have given us a signer algorithm oid instead of a digest
- * algorithm oid. This call will map to a signer oid to a digest one,
- * otherwise it leaves the oid alone and let the chips fall as they may
- * if it's not a digest oid.
- */
- algtag = NSS_CMSUtil_MapSignAlgs(algdata->offset);
- if (algtag != algdata->offset) {
- /* if the tags don't match, then we must have received a signer
- * algorithID. Now we need to get the oid data for the digest
- * oid, which the rest of the code is expecting */
- algdata = SECOID_FindOIDByTag(algtag);
- }
- return algdata;
- }
- SECOidTag
- NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo)
- {
- SECOidData *algdata;
- if (!signerinfo) {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SEC_OID_UNKNOWN;
- }
- algdata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
- if (algdata != NULL)
- return algdata->offset;
- else
- return SEC_OID_UNKNOWN;
- }
- CERTCertificateList *
- NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo)
- {
- return signerinfo->certList;
- }
- int
- NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo)
- {
- unsigned long version;
- /* always take apart the SECItem */
- if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
- return 0;
- else
- return (int)version;
- }
- /*
- * NSS_CMSSignerInfo_GetSigningTime - return the signing time,
- * in UTCTime or GeneralizedTime format,
- * of a CMS signerInfo.
- *
- * sinfo - signerInfo data for this signer
- *
- * Returns a pointer to XXXX (what?)
- * A return value of NULL is an error.
- */
- SECStatus
- NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime)
- {
- NSSCMSAttribute *attr;
- SECItem *value;
- if (sinfo == NULL)
- return SECFailure;
- if (sinfo->signingTime != 0) {
- *stime = sinfo->signingTime; /* cached copy */
- return SECSuccess;
- }
- attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr,
- SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
- /* XXXX multi-valued attributes NIH */
- if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL)
- return SECFailure;
- if (DER_DecodeTimeChoice(stime, value) != SECSuccess)
- return SECFailure;
- sinfo->signingTime = *stime; /* make cached copy */
- return SECSuccess;
- }
- /*
- * Return the signing cert of a CMS signerInfo.
- *
- * the certs in the enclosing SignedData must have been imported already
- */
- CERTCertificate *
- NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb)
- {
- CERTCertificate *cert;
- NSSCMSSignerIdentifier *sid;
- if (signerinfo->cert != NULL)
- return signerinfo->cert;
- /* no certdb, and cert hasn't been set yet? */
- if (certdb == NULL)
- return NULL;
- /*
- * This cert will also need to be freed, but since we save it
- * in signerinfo for later, we do not want to destroy it when
- * we leave this function -- we let the clean-up of the entire
- * cinfo structure later do the destroy of this cert.
- */
- sid = &signerinfo->signerIdentifier;
- switch (sid->identifierType) {
- case NSSCMSSignerID_IssuerSN:
- cert = CERT_FindCertByIssuerAndSN(certdb, sid->id.issuerAndSN);
- break;
- case NSSCMSSignerID_SubjectKeyID:
- cert = CERT_FindCertBySubjectKeyID(certdb, sid->id.subjectKeyID);
- break;
- default:
- cert = NULL;
- break;
- }
- /* cert can be NULL at that point */
- signerinfo->cert = cert; /* earmark it */
- return cert;
- }
- /*
- * NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer
- *
- * sinfo - signerInfo data for this signer
- *
- * Returns a pointer to allocated memory, which must be freed with PORT_Free.
- * A return value of NULL is an error.
- */
- char *
- NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo)
- {
- CERTCertificate *signercert;
- /* will fail if cert is not verified */
- if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
- return NULL;
- return (CERT_GetCommonName(&signercert->subject));
- }
- /*
- * NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer
- *
- * sinfo - signerInfo data for this signer
- *
- * Returns a pointer to allocated memory, which must be freed.
- * A return value of NULL is an error.
- */
- char *
- NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo)
- {
- CERTCertificate *signercert;
- if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
- return NULL;
- if (!signercert->emailAddr || !signercert->emailAddr[0])
- return NULL;
- return (PORT_Strdup(signercert->emailAddr));
- }
- /*
- * NSS_CMSSignerInfo_AddAuthAttr - add an attribute to the
- * authenticated (i.e. signed) attributes of "signerinfo".
- */
- SECStatus
- NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
- {
- return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
- }
- /*
- * NSS_CMSSignerInfo_AddUnauthAttr - add an attribute to the
- * unauthenticated attributes of "signerinfo".
- */
- SECStatus
- NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
- {
- return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
- }
- /*
- * NSS_CMSSignerInfo_AddSigningTime - add the signing time to the
- * authenticated (i.e. signed) attributes of "signerinfo".
- *
- * This is expected to be included in outgoing signed
- * messages for email (S/MIME) but is likely useful in other situations.
- *
- * This should only be added once; a second call will do nothing.
- *
- * XXX This will probably just shove the current time into "signerinfo"
- * but it will not actually get signed until the entire item is
- * processed for encoding. Is this (expected to be small) delay okay?
- */
- SECStatus
- NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t)
- {
- NSSCMSAttribute *attr;
- SECItem stime;
- void *mark;
- PLArenaPool *poolp;
- poolp = signerinfo->cmsg->poolp;
- mark = PORT_ArenaMark(poolp);
- /* create new signing time attribute */
- if (DER_EncodeTimeChoice(NULL, &stime, t) != SECSuccess)
- goto loser;
- if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
- SECITEM_FreeItem(&stime, PR_FALSE);
- goto loser;
- }
- SECITEM_FreeItem(&stime, PR_FALSE);
- if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
- goto loser;
- PORT_ArenaUnmark(poolp, mark);
- return SECSuccess;
- loser:
- PORT_ArenaRelease(poolp, mark);
- return SECFailure;
- }
- /*
- * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the
- * authenticated (i.e. signed) attributes of "signerinfo".
- *
- * This is expected to be included in outgoing signed
- * messages for email (S/MIME).
- */
- SECStatus
- NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo)
- {
- NSSCMSAttribute *attr;
- SECItem *smimecaps = NULL;
- void *mark;
- PLArenaPool *poolp;
- poolp = signerinfo->cmsg->poolp;
- mark = PORT_ArenaMark(poolp);
- smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
- if (smimecaps == NULL)
- goto loser;
- /* create new signing time attribute */
- if (NSS_SMIMEUtil_CreateSMIMECapabilities(poolp, smimecaps) != SECSuccess)
- goto loser;
- if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
- goto loser;
- if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
- goto loser;
- PORT_ArenaUnmark(poolp, mark);
- return SECSuccess;
- loser:
- PORT_ArenaRelease(poolp, mark);
- return SECFailure;
- }
- /*
- * NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
- * authenticated (i.e. signed) attributes of "signerinfo".
- *
- * This is expected to be included in outgoing signed messages for email (S/MIME).
- */
- SECStatus
- NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
- {
- NSSCMSAttribute *attr;
- SECItem *smimeekp = NULL;
- void *mark;
- PLArenaPool *poolp;
- /* verify this cert for encryption */
- if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
- return SECFailure;
- }
- poolp = signerinfo->cmsg->poolp;
- mark = PORT_ArenaMark(poolp);
- smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
- if (smimeekp == NULL)
- goto loser;
- /* create new signing time attribute */
- if (NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
- goto loser;
- if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
- goto loser;
- if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
- goto loser;
- PORT_ArenaUnmark(poolp, mark);
- return SECSuccess;
- loser:
- PORT_ArenaRelease(poolp, mark);
- return SECFailure;
- }
- /*
- * NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
- * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
- *
- * This is expected to be included in outgoing signed messages for email (S/MIME),
- * if compatibility with Microsoft mail clients is wanted.
- */
- SECStatus
- NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
- {
- NSSCMSAttribute *attr;
- SECItem *smimeekp = NULL;
- void *mark;
- PLArenaPool *poolp;
- /* verify this cert for encryption */
- if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
- return SECFailure;
- }
- poolp = signerinfo->cmsg->poolp;
- mark = PORT_ArenaMark(poolp);
- smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
- if (smimeekp == NULL)
- goto loser;
- /* create new signing time attribute */
- if (NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
- goto loser;
- if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
- goto loser;
- if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
- goto loser;
- PORT_ArenaUnmark(poolp, mark);
- return SECSuccess;
- loser:
- PORT_ArenaRelease(poolp, mark);
- return SECFailure;
- }
- /*
- * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo
- *
- * 1. digest the DER-encoded signature value of the original signerinfo
- * 2. create new signerinfo with correct version, sid, digestAlg
- * 3. add message-digest authAttr, but NO content-type
- * 4. sign the authAttrs
- * 5. DER-encode the new signerInfo
- * 6. add the whole thing to original signerInfo's unAuthAttrs
- * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
- *
- * XXXX give back the new signerinfo?
- */
- SECStatus
- NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo,
- SECOidTag digestalg, CERTCertificate signingcert)
- {
- /* XXXX TBD XXXX */
- return SECFailure;
- }
- /*
- * XXXX the following needs to be done in the S/MIME layer code
- * after signature of a signerinfo is verified
- */
- SECStatus
- NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo)
- {
- CERTCertificate *cert = NULL;
- SECItem *profile = NULL;
- NSSCMSAttribute *attr;
- SECItem *stime = NULL;
- SECItem *ekp;
- CERTCertDBHandle *certdb;
- int save_error;
- SECStatus rv;
- PRBool must_free_cert = PR_FALSE;
- certdb = CERT_GetDefaultCertDB();
- /* sanity check - see if verification status is ok (unverified does not count...) */
- if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature)
- return SECFailure;
- /* find preferred encryption cert */
- if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) &&
- (attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
- SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
- ekp = NSS_CMSAttribute_GetValue(attr);
- if (ekp == NULL)
- return SECFailure;
- /* we assume that all certs coming with the message have been imported to the */
- /* temporary database */
- cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp);
- if (cert == NULL)
- return SECFailure;
- must_free_cert = PR_TRUE;
- }
- if (cert == NULL) {
- /* no preferred cert found?
- * find the cert the signerinfo is signed with instead */
- cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb);
- if (cert == NULL || cert->emailAddr == NULL || !cert->emailAddr[0])
- return SECFailure;
- }
- /* verify this cert for encryption (has been verified for signing so far) */
- /* don't verify this cert for encryption. It may just be a signing cert.
- * that's OK, we can still save the S/MIME profile. The encryption cert
- * should have already been saved */
- #ifdef notdef
- if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
- if (must_free_cert)
- CERT_DestroyCertificate(cert);
- return SECFailure;
- }
- #endif
- /* XXX store encryption cert permanently? */
- /*
- * Remember the current error set because we do not care about
- * anything set by the functions we are about to call.
- */
- save_error = PORT_GetError();
- if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
- attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
- SEC_OID_PKCS9_SMIME_CAPABILITIES,
- PR_TRUE);
- profile = NSS_CMSAttribute_GetValue(attr);
- attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
- SEC_OID_PKCS9_SIGNING_TIME,
- PR_TRUE);
- stime = NSS_CMSAttribute_GetValue(attr);
- }
- rv = CERT_SaveSMimeProfile(cert, profile, stime);
- if (must_free_cert)
- CERT_DestroyCertificate(cert);
- /*
- * Restore the saved error in case the calls above set a new
- * one that we do not actually care about.
- */
- PORT_SetError(save_error);
- return rv;
- }
- /*
- * NSS_CMSSignerInfo_IncludeCerts - set cert chain inclusion mode for this signer
- */
- SECStatus
- NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo *signerinfo,
- NSSCMSCertChainMode cm, SECCertUsage usage)
- {
- if (signerinfo->cert == NULL)
- return SECFailure;
- /* don't leak if we get called twice */
- if (signerinfo->certList != NULL) {
- CERT_DestroyCertificateList(signerinfo->certList);
- signerinfo->certList = NULL;
- }
- switch (cm) {
- case NSSCMSCM_None:
- signerinfo->certList = NULL;
- break;
- case NSSCMSCM_CertOnly:
- signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
- break;
- case NSSCMSCM_CertChain:
- signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
- usage, PR_FALSE);
- break;
- case NSSCMSCM_CertChainWithRoot:
- signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
- usage, PR_TRUE);
- break;
- }
- if (cm != NSSCMSCM_None && signerinfo->certList == NULL)
- return SECFailure;
- return SECSuccess;
- }
|