SECStatus NSS_CMSSignedData_AddSignerInfo(NSSCMSSignedData *sigd, NSSCMSSignerInfo *signerinfo) { void *mark; SECStatus rv; SECOidTag digestalgtag; PLArenaPool *poolp; if (!sigd || !signerinfo) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } poolp = sigd->cmsg->poolp; mark = PORT_ArenaMark(poolp); /* add signerinfo */ rv = NSS_CMSArray_Add(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo); if (rv != SECSuccess) goto loser; /* * add empty digest * Empty because we don't have it yet. Either it gets created during encoding * (if the data is present) or has to be set externally. * XXX maybe pass it in optionally? */ digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo); rv = NSS_CMSSignedData_SetDigestValue(sigd, digestalgtag, NULL); if (rv != SECSuccess) goto loser; /* * The last thing to get consistency would be adding the digest. */ PORT_ArenaUnmark(poolp, mark); return SECSuccess; loser: PORT_ArenaRelease(poolp, mark); return SECFailure; }
NS_IMETHODIMP nsCMSMessage::CreateSigned(nsIX509Cert* aSigningCert, nsIX509Cert* aEncryptCert, unsigned char* aDigestData, PRUint32 aDigestDataLen) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned\n")); NSSCMSContentInfo *cinfo; NSSCMSSignedData *sigd; NSSCMSSignerInfo *signerinfo; CERTCertificate *scert = nsnull, *ecert = nsnull; nsCOMPtr<nsIX509Cert2> aSigningCert2 = do_QueryInterface(aSigningCert); nsresult rv = NS_ERROR_FAILURE; /* Get the certs */ if (aSigningCert2) { scert = aSigningCert2->GetCert(); } if (!scert) { return NS_ERROR_FAILURE; } if (aEncryptCert) { nsCOMPtr<nsIX509Cert2> aEncryptCert2 = do_QueryInterface(aEncryptCert); if (aEncryptCert2) { ecert = aEncryptCert2->GetCert(); } } CERTCertificateCleaner ecertCleaner(ecert); CERTCertificateCleaner scertCleaner(scert); /* * create the message object */ m_cmsMsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */ if (m_cmsMsg == NULL) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create new message\n")); rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } /* * build chain of objects: message->signedData->data */ if ((sigd = NSS_CMSSignedData_Create(m_cmsMsg)) == NULL) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create signed data\n")); goto loser; } cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg); if (NSS_CMSContentInfo_SetContent_SignedData(m_cmsMsg, cinfo, sigd) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set content signed data\n")); goto loser; } cinfo = NSS_CMSSignedData_GetContentInfo(sigd); /* we're always passing data in and detaching optionally */ if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nsnull, PR_TRUE) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set content data\n")); goto loser; } /* * create & attach signer information */ if ((signerinfo = NSS_CMSSignerInfo_Create(m_cmsMsg, scert, SEC_OID_SHA1)) == NULL) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create signer info\n")); goto loser; } /* we want the cert chain included for this one */ if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't include signer cert chain\n")); goto loser; } if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add signing time\n")); goto loser; } if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add smime caps\n")); goto loser; } if (ecert) { if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ecert, CERT_GetDefaultCertDB()) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add smime enc key prefs\n")); goto loser; } if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ecert, CERT_GetDefaultCertDB()) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add MS smime enc key prefs\n")); goto loser; } // If signing and encryption cert are identical, don't add it twice. PRBool addEncryptionCert = (ecert && (!scert || !CERT_CompareCerts(ecert, scert))); if (addEncryptionCert && NSS_CMSSignedData_AddCertificate(sigd, ecert) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add own encryption certificate\n")); goto loser; } } if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add signer info\n")); goto loser; } // Finally, add the pre-computed digest if passed in if (aDigestData) { SECItem digest; digest.data = aDigestData; digest.len = aDigestDataLen; if (NSS_CMSSignedData_SetDigestValue(sigd, SEC_OID_SHA1, &digest)) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set digest value\n")); goto loser; } } return NS_OK; loser: if (m_cmsMsg) { NSS_CMSMessage_Destroy(m_cmsMsg); m_cmsMsg = nsnull; } return rv; }
nsresult nsCMSMessage::CommonVerifySignature(unsigned char* aDigestData, PRUint32 aDigestDataLen) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature, content level count %d\n", NSS_CMSMessage_ContentLevelCount(m_cmsMsg))); NSSCMSContentInfo *cinfo = nsnull; NSSCMSSignedData *sigd = nsnull; NSSCMSSignerInfo *si; PRInt32 nsigners; nsresult rv = NS_ERROR_FAILURE; if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - not signed\n")); return NS_ERROR_CMS_VERIFY_NOT_SIGNED; } cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0); if (cinfo) { // I don't like this hard cast. We should check in some way, that we really have this type. sigd = (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo); } if (!sigd) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - no content info\n")); rv = NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO; goto loser; } if (aDigestData && aDigestDataLen) { SECItem digest; digest.data = aDigestData; digest.len = aDigestDataLen; if (NSS_CMSSignedData_SetDigestValue(sigd, SEC_OID_SHA1, &digest)) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - bad digest\n")); rv = NS_ERROR_CMS_VERIFY_BAD_DIGEST; goto loser; } } // Import certs. Note that import failure is not a signature verification failure. // if (NSS_CMSSignedData_ImportCerts(sigd, CERT_GetDefaultCertDB(), certUsageEmailRecipient, PR_TRUE) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - can not import certs\n")); } nsigners = NSS_CMSSignedData_SignerInfoCount(sigd); PR_ASSERT(nsigners > 0); si = NSS_CMSSignedData_GetSignerInfo(sigd, 0); // See bug 324474. We want to make sure the signing cert is // still valid at the current time. if (CERT_VerifyCertificateNow(CERT_GetDefaultCertDB(), si->cert, PR_TRUE, certificateUsageEmailSigner, si->cmsg->pwfn_arg, NULL) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - signing cert not trusted now\n")); rv = NS_ERROR_CMS_VERIFY_UNTRUSTED; goto loser; } // We verify the first signer info, only // if (NSS_CMSSignedData_VerifySignerInfo(sigd, 0, CERT_GetDefaultCertDB(), certUsageEmailSigner) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - unable to verify signature\n")); if (NSSCMSVS_SigningCertNotFound == si->verificationStatus) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - signing cert not found\n")); rv = NS_ERROR_CMS_VERIFY_NOCERT; } else if(NSSCMSVS_SigningCertNotTrusted == si->verificationStatus) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - signing cert not trusted at signing time\n")); rv = NS_ERROR_CMS_VERIFY_UNTRUSTED; } else if(NSSCMSVS_Unverified == si->verificationStatus) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - can not verify\n")); rv = NS_ERROR_CMS_VERIFY_ERROR_UNVERIFIED; } else if(NSSCMSVS_ProcessingError == si->verificationStatus) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - processing error\n")); rv = NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } else if(NSSCMSVS_BadSignature == si->verificationStatus) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - bad signature\n")); rv = NS_ERROR_CMS_VERIFY_BAD_SIGNATURE; } else if(NSSCMSVS_DigestMismatch == si->verificationStatus) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - digest mismatch\n")); rv = NS_ERROR_CMS_VERIFY_DIGEST_MISMATCH; } else if(NSSCMSVS_SignatureAlgorithmUnknown == si->verificationStatus) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - algo unknown\n")); rv = NS_ERROR_CMS_VERIFY_UNKNOWN_ALGO; } else if(NSSCMSVS_SignatureAlgorithmUnsupported == si->verificationStatus) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - algo not supported\n")); rv = NS_ERROR_CMS_VERIFY_UNSUPPORTED_ALGO; } else if(NSSCMSVS_MalformedSignature == si->verificationStatus) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - malformed signature\n")); rv = NS_ERROR_CMS_VERIFY_MALFORMED_SIGNATURE; } goto loser; } // Save the profile. Note that save import failure is not a signature verification failure. // if (NSS_SMIMESignerInfo_SaveSMIMEProfile(si) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - unable to save smime profile\n")); } rv = NS_OK; loser: return rv; }
nsresult VerifyCMSDetachedSignatureIncludingCertificate( const SECItem& buffer, const SECItem& detachedDigest, nsresult (*verifyCertificate)(CERTCertificate* cert, void* context, void* pinArg), void *verifyCertificateContext, void* pinArg) { // XXX: missing pinArg is tolerated. if (NS_WARN_IF(!buffer.data && buffer.len > 0) || NS_WARN_IF(!detachedDigest.data && detachedDigest.len > 0) || (!verifyCertificate) || NS_WARN_IF(!verifyCertificateContext)) { return NS_ERROR_INVALID_ARG; } ScopedNSSCMSMessage cmsMsg(NSS_CMSMessage_CreateFromDER(const_cast<SECItem*>(&buffer), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)); if (!cmsMsg) { return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } if (!NSS_CMSMessage_IsSigned(cmsMsg.get())) { return NS_ERROR_CMS_VERIFY_NOT_SIGNED; } NSSCMSContentInfo* cinfo = NSS_CMSMessage_ContentLevel(cmsMsg.get(), 0); if (!cinfo) { return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO; } // signedData is non-owning NSSCMSSignedData* signedData = reinterpret_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(cinfo)); if (!signedData) { return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO; } // Set digest value. if (NSS_CMSSignedData_SetDigestValue(signedData, SEC_OID_SHA1, const_cast<SECItem*>(&detachedDigest))) { return NS_ERROR_CMS_VERIFY_BAD_DIGEST; } // Parse the certificates into CERTCertificate objects held in memory so // verifyCertificate will be able to find them during path building. ScopedCERTCertList certs(CERT_NewCertList()); if (!certs) { return NS_ERROR_OUT_OF_MEMORY; } if (signedData->rawCerts) { for (size_t i = 0; signedData->rawCerts[i]; ++i) { ScopedCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), signedData->rawCerts[i], nullptr, false, true)); // Skip certificates that fail to parse if (cert) { if (CERT_AddCertToListTail(certs.get(), cert.get()) == SECSuccess) { cert.forget(); // ownership transfered } else { return NS_ERROR_OUT_OF_MEMORY; } } } } // Get the end-entity certificate. int numSigners = NSS_CMSSignedData_SignerInfoCount(signedData); if (NS_WARN_IF(numSigners != 1)) { return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } // signer is non-owning. NSSCMSSignerInfo* signer = NSS_CMSSignedData_GetSignerInfo(signedData, 0); if (NS_WARN_IF(!signer)) { return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } CERTCertificate* signerCert = NSS_CMSSignerInfo_GetSigningCertificate(signer, CERT_GetDefaultCertDB()); if (!signerCert) { return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } nsresult rv = verifyCertificate(signerCert, verifyCertificateContext, pinArg); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } // See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS. SECOidData* contentTypeOidData = SECOID_FindOID(&signedData->contentInfo.contentType); if (!contentTypeOidData) { return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } return MapSECStatus(NSS_CMSSignerInfo_Verify(signer, const_cast<SECItem*>(&detachedDigest), &contentTypeOidData->oid)); }