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