michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: #include "nsCMS.h" michael@0: michael@0: #include "CertVerifier.h" michael@0: #include "pkix/pkixtypes.h" michael@0: #include "nsISupports.h" michael@0: #include "nsNSSHelper.h" michael@0: #include "nsNSSCertificate.h" michael@0: #include "smime.h" michael@0: #include "cms.h" michael@0: #include "nsICMSMessageErrors.h" michael@0: #include "nsIArray.h" michael@0: #include "nsArrayUtils.h" michael@0: #include "nsCertVerificationThread.h" michael@0: michael@0: #include "prlog.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::psm; michael@0: michael@0: #ifdef PR_LOGGING michael@0: extern PRLogModuleInfo* gPIPNSSLog; michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCMSMessage, nsICMSMessage, nsICMSMessage2) michael@0: michael@0: nsCMSMessage::nsCMSMessage() michael@0: { michael@0: m_cmsMsg = nullptr; michael@0: } michael@0: nsCMSMessage::nsCMSMessage(NSSCMSMessage *aCMSMsg) michael@0: { michael@0: m_cmsMsg = aCMSMsg; michael@0: } michael@0: michael@0: nsCMSMessage::~nsCMSMessage() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) { michael@0: return; michael@0: } michael@0: destructorSafeDestroyNSSReference(); michael@0: shutdown(calledFromObject); michael@0: } michael@0: michael@0: void nsCMSMessage::virtualDestroyNSSReference() michael@0: { michael@0: destructorSafeDestroyNSSReference(); michael@0: } michael@0: michael@0: void nsCMSMessage::destructorSafeDestroyNSSReference() michael@0: { michael@0: if (m_cmsMsg) { michael@0: NSS_CMSMessage_Destroy(m_cmsMsg); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::VerifySignature() michael@0: { michael@0: return CommonVerifySignature(nullptr, 0); michael@0: } michael@0: michael@0: NSSCMSSignerInfo* nsCMSMessage::GetTopLevelSignerInfo() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return nullptr; michael@0: michael@0: if (!m_cmsMsg) michael@0: return nullptr; michael@0: michael@0: if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) michael@0: return nullptr; michael@0: michael@0: NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0); michael@0: if (!cinfo) michael@0: return nullptr; michael@0: michael@0: NSSCMSSignedData *sigd = (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo); michael@0: if (!sigd) michael@0: return nullptr; michael@0: michael@0: PR_ASSERT(NSS_CMSSignedData_SignerInfoCount(sigd) > 0); michael@0: return NSS_CMSSignedData_GetSignerInfo(sigd, 0); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::GetSignerEmailAddress(char * * aEmail) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerEmailAddress\n")); michael@0: NS_ENSURE_ARG(aEmail); michael@0: michael@0: NSSCMSSignerInfo *si = GetTopLevelSignerInfo(); michael@0: if (!si) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: *aEmail = NSS_CMSSignerInfo_GetSignerEmailAddress(si); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::GetSignerCommonName(char ** aName) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerCommonName\n")); michael@0: NS_ENSURE_ARG(aName); michael@0: michael@0: NSSCMSSignerInfo *si = GetTopLevelSignerInfo(); michael@0: if (!si) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: *aName = NSS_CMSSignerInfo_GetSignerCommonName(si); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::ContentIsEncrypted(bool *isEncrypted) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::ContentIsEncrypted\n")); michael@0: NS_ENSURE_ARG(isEncrypted); michael@0: michael@0: if (!m_cmsMsg) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: *isEncrypted = NSS_CMSMessage_IsEncrypted(m_cmsMsg); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::ContentIsSigned(bool *isSigned) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::ContentIsSigned\n")); michael@0: NS_ENSURE_ARG(isSigned); michael@0: michael@0: if (!m_cmsMsg) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: *isSigned = NSS_CMSMessage_IsSigned(m_cmsMsg); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::GetSignerCert(nsIX509Cert **scert) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: NSSCMSSignerInfo *si = GetTopLevelSignerInfo(); michael@0: if (!si) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (si->cert) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerCert got signer cert\n")); michael@0: michael@0: *scert = nsNSSCertificate::Create(si->cert); michael@0: if (*scert) { michael@0: (*scert)->AddRef(); michael@0: } michael@0: } michael@0: else { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerCert no signer cert, do we have a cert list? %s\n", michael@0: (si->certList ? "yes" : "no") )); michael@0: michael@0: *scert = nullptr; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::GetEncryptionCert(nsIX509Cert **ecert) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::VerifyDetachedSignature(unsigned char* aDigestData, uint32_t aDigestDataLen) michael@0: { michael@0: if (!aDigestData || !aDigestDataLen) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return CommonVerifySignature(aDigestData, aDigestDataLen); michael@0: } michael@0: michael@0: nsresult nsCMSMessage::CommonVerifySignature(unsigned char* aDigestData, uint32_t aDigestDataLen) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature, content level count %d\n", NSS_CMSMessage_ContentLevelCount(m_cmsMsg))); michael@0: NSSCMSContentInfo *cinfo = nullptr; michael@0: NSSCMSSignedData *sigd = nullptr; michael@0: NSSCMSSignerInfo *si; michael@0: int32_t nsigners; michael@0: RefPtr certVerifier; michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - not signed\n")); michael@0: return NS_ERROR_CMS_VERIFY_NOT_SIGNED; michael@0: } michael@0: michael@0: cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0); michael@0: if (cinfo) { michael@0: // I don't like this hard cast. We should check in some way, that we really have this type. michael@0: sigd = (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo); michael@0: } michael@0: michael@0: if (!sigd) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - no content info\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO; michael@0: goto loser; michael@0: } michael@0: michael@0: if (aDigestData && aDigestDataLen) michael@0: { michael@0: SECItem digest; michael@0: digest.data = aDigestData; michael@0: digest.len = aDigestDataLen; michael@0: michael@0: if (NSS_CMSSignedData_SetDigestValue(sigd, SEC_OID_SHA1, &digest)) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - bad digest\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_BAD_DIGEST; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: // Import certs. Note that import failure is not a signature verification failure. // michael@0: if (NSS_CMSSignedData_ImportCerts(sigd, CERT_GetDefaultCertDB(), certUsageEmailRecipient, true) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - can not import certs\n")); michael@0: } michael@0: michael@0: nsigners = NSS_CMSSignedData_SignerInfoCount(sigd); michael@0: PR_ASSERT(nsigners > 0); michael@0: NS_ENSURE_TRUE(nsigners > 0, NS_ERROR_UNEXPECTED); michael@0: si = NSS_CMSSignedData_GetSignerInfo(sigd, 0); michael@0: michael@0: // See bug 324474. We want to make sure the signing cert is michael@0: // still valid at the current time. michael@0: michael@0: certVerifier = GetDefaultCertVerifier(); michael@0: NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED); michael@0: michael@0: { michael@0: SECStatus srv = certVerifier->VerifyCert(si->cert, michael@0: certificateUsageEmailSigner, michael@0: PR_Now(), nullptr /*XXX pinarg*/, michael@0: nullptr /*hostname*/); michael@0: if (srv != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, michael@0: ("nsCMSMessage::CommonVerifySignature - signing cert not trusted now\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_UNTRUSTED; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: // We verify the first signer info, only // michael@0: if (NSS_CMSSignedData_VerifySignerInfo(sigd, 0, CERT_GetDefaultCertDB(), certUsageEmailSigner) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - unable to verify signature\n")); michael@0: michael@0: if (NSSCMSVS_SigningCertNotFound == si->verificationStatus) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - signing cert not found\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_NOCERT; michael@0: } michael@0: else if(NSSCMSVS_SigningCertNotTrusted == si->verificationStatus) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - signing cert not trusted at signing time\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_UNTRUSTED; michael@0: } michael@0: else if(NSSCMSVS_Unverified == si->verificationStatus) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - can not verify\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_ERROR_UNVERIFIED; michael@0: } michael@0: else if(NSSCMSVS_ProcessingError == si->verificationStatus) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - processing error\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; michael@0: } michael@0: else if(NSSCMSVS_BadSignature == si->verificationStatus) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - bad signature\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_BAD_SIGNATURE; michael@0: } michael@0: else if(NSSCMSVS_DigestMismatch == si->verificationStatus) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - digest mismatch\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_DIGEST_MISMATCH; michael@0: } michael@0: else if(NSSCMSVS_SignatureAlgorithmUnknown == si->verificationStatus) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - algo unknown\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_UNKNOWN_ALGO; michael@0: } michael@0: else if(NSSCMSVS_SignatureAlgorithmUnsupported == si->verificationStatus) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - algo not supported\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_UNSUPPORTED_ALGO; michael@0: } michael@0: else if(NSSCMSVS_MalformedSignature == si->verificationStatus) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - malformed signature\n")); michael@0: rv = NS_ERROR_CMS_VERIFY_MALFORMED_SIGNATURE; michael@0: } michael@0: michael@0: goto loser; michael@0: } michael@0: michael@0: // Save the profile. Note that save import failure is not a signature verification failure. // michael@0: if (NSS_SMIMESignerInfo_SaveSMIMEProfile(si) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - unable to save smime profile\n")); michael@0: } michael@0: michael@0: rv = NS_OK; michael@0: loser: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::AsyncVerifySignature( michael@0: nsISMimeVerificationListener *aListener) michael@0: { michael@0: return CommonAsyncVerifySignature(aListener, nullptr, 0); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::AsyncVerifyDetachedSignature( michael@0: nsISMimeVerificationListener *aListener, michael@0: unsigned char* aDigestData, uint32_t aDigestDataLen) michael@0: { michael@0: if (!aDigestData || !aDigestDataLen) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return CommonAsyncVerifySignature(aListener, aDigestData, aDigestDataLen); michael@0: } michael@0: michael@0: nsresult nsCMSMessage::CommonAsyncVerifySignature(nsISMimeVerificationListener *aListener, michael@0: unsigned char* aDigestData, uint32_t aDigestDataLen) michael@0: { michael@0: nsSMimeVerificationJob *job = new nsSMimeVerificationJob; michael@0: michael@0: if (aDigestData) michael@0: { michael@0: job->digest_data = new unsigned char[aDigestDataLen]; michael@0: memcpy(job->digest_data, aDigestData, aDigestDataLen); michael@0: } michael@0: else michael@0: { michael@0: job->digest_data = nullptr; michael@0: } michael@0: michael@0: job->digest_len = aDigestDataLen; michael@0: job->mMessage = this; michael@0: job->mListener = aListener; michael@0: michael@0: nsresult rv = nsCertVerificationThread::addJob(job); michael@0: if (NS_FAILED(rv)) michael@0: delete job; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: class nsZeroTerminatedCertArray : public nsNSSShutDownObject michael@0: { michael@0: public: michael@0: nsZeroTerminatedCertArray() michael@0: :mCerts(nullptr), mPoolp(nullptr), mSize(0) michael@0: { michael@0: } michael@0: michael@0: ~nsZeroTerminatedCertArray() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) { michael@0: return; michael@0: } michael@0: destructorSafeDestroyNSSReference(); michael@0: shutdown(calledFromObject); michael@0: } michael@0: michael@0: void virtualDestroyNSSReference() michael@0: { michael@0: destructorSafeDestroyNSSReference(); michael@0: } michael@0: michael@0: void destructorSafeDestroyNSSReference() michael@0: { michael@0: if (mCerts) michael@0: { michael@0: for (uint32_t i=0; i < mSize; i++) { michael@0: if (mCerts[i]) { michael@0: CERT_DestroyCertificate(mCerts[i]); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (mPoolp) michael@0: PORT_FreeArena(mPoolp, false); michael@0: } michael@0: michael@0: bool allocate(uint32_t count) michael@0: { michael@0: // only allow allocation once michael@0: if (mPoolp) michael@0: return false; michael@0: michael@0: mSize = count; michael@0: michael@0: if (!mSize) michael@0: return false; michael@0: michael@0: mPoolp = PORT_NewArena(1024); michael@0: if (!mPoolp) michael@0: return false; michael@0: michael@0: mCerts = (CERTCertificate**)PORT_ArenaZAlloc( michael@0: mPoolp, (count+1)*sizeof(CERTCertificate*)); michael@0: michael@0: if (!mCerts) michael@0: return false; michael@0: michael@0: // null array, including zero termination michael@0: for (uint32_t i = 0; i < count+1; i++) { michael@0: mCerts[i] = nullptr; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void set(uint32_t i, CERTCertificate *c) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return; michael@0: michael@0: if (i >= mSize) michael@0: return; michael@0: michael@0: if (mCerts[i]) { michael@0: CERT_DestroyCertificate(mCerts[i]); michael@0: } michael@0: michael@0: mCerts[i] = CERT_DupCertificate(c); michael@0: } michael@0: michael@0: CERTCertificate *get(uint32_t i) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return nullptr; michael@0: michael@0: if (i >= mSize) michael@0: return nullptr; michael@0: michael@0: return CERT_DupCertificate(mCerts[i]); michael@0: } michael@0: michael@0: CERTCertificate **getRawArray() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return nullptr; michael@0: michael@0: return mCerts; michael@0: } michael@0: michael@0: private: michael@0: CERTCertificate **mCerts; michael@0: PLArenaPool *mPoolp; michael@0: uint32_t mSize; michael@0: }; michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::CreateEncrypted(nsIArray * aRecipientCerts) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted\n")); michael@0: NSSCMSContentInfo *cinfo; michael@0: NSSCMSEnvelopedData *envd; michael@0: NSSCMSRecipientInfo *recipientInfo; michael@0: nsZeroTerminatedCertArray recipientCerts; michael@0: SECOidTag bulkAlgTag; michael@0: int keySize; michael@0: uint32_t i; michael@0: nsCOMPtr nssRecipientCert; michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: // Check the recipient certificates // michael@0: uint32_t recipientCertCount; michael@0: aRecipientCerts->GetLength(&recipientCertCount); michael@0: PR_ASSERT(recipientCertCount > 0); michael@0: michael@0: if (!recipientCerts.allocate(recipientCertCount)) { michael@0: goto loser; michael@0: } michael@0: michael@0: for (i=0; i x509cert = do_QueryElementAt(aRecipientCerts, i); michael@0: michael@0: nssRecipientCert = do_QueryInterface(x509cert); michael@0: michael@0: if (!nssRecipientCert) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: mozilla::pkix::ScopedCERTCertificate c(nssRecipientCert->GetCert()); michael@0: recipientCerts.set(i, c.get()); michael@0: } michael@0: michael@0: // Find a bulk key algorithm // michael@0: if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientCerts.getRawArray(), &bulkAlgTag, michael@0: &keySize) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't find bulk alg for recipients\n")); michael@0: rv = NS_ERROR_CMS_ENCRYPT_NO_BULK_ALG; michael@0: goto loser; michael@0: } michael@0: michael@0: m_cmsMsg = NSS_CMSMessage_Create(nullptr); michael@0: if (!m_cmsMsg) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create new cms message\n")); michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: if ((envd = NSS_CMSEnvelopedData_Create(m_cmsMsg, bulkAlgTag, keySize)) == nullptr) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create enveloped data\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg); michael@0: if (NSS_CMSContentInfo_SetContent_EnvelopedData(m_cmsMsg, cinfo, envd) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create content enveloped data\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd); michael@0: if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nullptr, false) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't set content data\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: // Create and attach recipient information // michael@0: for (i=0; i < recipientCertCount; i++) { michael@0: mozilla::pkix::ScopedCERTCertificate rc(recipientCerts.get(i)); michael@0: if ((recipientInfo = NSS_CMSRecipientInfo_Create(m_cmsMsg, rc.get())) == nullptr) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create recipient info\n")); michael@0: goto loser; michael@0: } michael@0: if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientInfo) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't add recipient info\n")); michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: loser: michael@0: if (m_cmsMsg) { michael@0: NSS_CMSMessage_Destroy(m_cmsMsg); michael@0: m_cmsMsg = nullptr; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCMSMessage::CreateSigned(nsIX509Cert* aSigningCert, nsIX509Cert* aEncryptCert, unsigned char* aDigestData, uint32_t aDigestDataLen) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned\n")); michael@0: NSSCMSContentInfo *cinfo; michael@0: NSSCMSSignedData *sigd; michael@0: NSSCMSSignerInfo *signerinfo; michael@0: mozilla::pkix::ScopedCERTCertificate scert; michael@0: mozilla::pkix::ScopedCERTCertificate ecert; michael@0: nsCOMPtr aSigningCert2 = do_QueryInterface(aSigningCert); michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: /* Get the certs */ michael@0: if (aSigningCert2) { michael@0: scert = aSigningCert2->GetCert(); michael@0: } michael@0: if (!scert) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (aEncryptCert) { michael@0: nsCOMPtr aEncryptCert2 = do_QueryInterface(aEncryptCert); michael@0: if (aEncryptCert2) { michael@0: ecert = aEncryptCert2->GetCert(); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * create the message object michael@0: */ michael@0: m_cmsMsg = NSS_CMSMessage_Create(nullptr); /* create a message on its own pool */ michael@0: if (!m_cmsMsg) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create new message\n")); michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: * build chain of objects: message->signedData->data michael@0: */ michael@0: if ((sigd = NSS_CMSSignedData_Create(m_cmsMsg)) == nullptr) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create signed data\n")); michael@0: goto loser; michael@0: } michael@0: cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg); michael@0: if (NSS_CMSContentInfo_SetContent_SignedData(m_cmsMsg, cinfo, sigd) michael@0: != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set content signed data\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: cinfo = NSS_CMSSignedData_GetContentInfo(sigd); michael@0: michael@0: /* we're always passing data in and detaching optionally */ michael@0: if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nullptr, true) michael@0: != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set content data\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: * create & attach signer information michael@0: */ michael@0: if ((signerinfo = NSS_CMSSignerInfo_Create(m_cmsMsg, scert.get(), SEC_OID_SHA1)) michael@0: == nullptr) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create signer info\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: /* we want the cert chain included for this one */ michael@0: if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, michael@0: certUsageEmailSigner) michael@0: != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't include signer cert chain\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) michael@0: != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add signing time\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add smime caps\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: if (ecert) { michael@0: if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ecert.get(), michael@0: CERT_GetDefaultCertDB()) michael@0: != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add smime enc key prefs\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ecert.get(), michael@0: CERT_GetDefaultCertDB()) michael@0: != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add MS smime enc key prefs\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: // If signing and encryption cert are identical, don't add it twice. michael@0: bool addEncryptionCert = michael@0: (ecert && (!scert || !CERT_CompareCerts(ecert.get(), scert.get()))); michael@0: michael@0: if (addEncryptionCert && michael@0: NSS_CMSSignedData_AddCertificate(sigd, ecert.get()) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add own encryption certificate\n")); michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add signer info\n")); michael@0: goto loser; michael@0: } michael@0: michael@0: // Finally, add the pre-computed digest if passed in michael@0: if (aDigestData) { michael@0: SECItem digest; michael@0: michael@0: digest.data = aDigestData; michael@0: digest.len = aDigestDataLen; michael@0: michael@0: if (NSS_CMSSignedData_SetDigestValue(sigd, SEC_OID_SHA1, &digest)) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set digest value\n")); michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: loser: michael@0: if (m_cmsMsg) { michael@0: NSS_CMSMessage_Destroy(m_cmsMsg); michael@0: m_cmsMsg = nullptr; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCMSDecoder, nsICMSDecoder) michael@0: michael@0: nsCMSDecoder::nsCMSDecoder() michael@0: : m_dcx(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsCMSDecoder::~nsCMSDecoder() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) { michael@0: return; michael@0: } michael@0: destructorSafeDestroyNSSReference(); michael@0: shutdown(calledFromObject); michael@0: } michael@0: michael@0: void nsCMSDecoder::virtualDestroyNSSReference() michael@0: { michael@0: destructorSafeDestroyNSSReference(); michael@0: } michael@0: michael@0: void nsCMSDecoder::destructorSafeDestroyNSSReference() michael@0: { michael@0: if (m_dcx) { michael@0: NSS_CMSDecoder_Cancel(m_dcx); michael@0: m_dcx = nullptr; michael@0: } michael@0: } michael@0: michael@0: /* void start (in NSSCMSContentCallback cb, in voidPtr arg); */ michael@0: NS_IMETHODIMP nsCMSDecoder::Start(NSSCMSContentCallback cb, void * arg) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSDecoder::Start\n")); michael@0: m_ctx = new PipUIContext(); michael@0: michael@0: m_dcx = NSS_CMSDecoder_Start(0, cb, arg, 0, m_ctx, 0, 0); michael@0: if (!m_dcx) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSDecoder::Start - can't start decoder\n")); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void update (in string bug, in long len); */ michael@0: NS_IMETHODIMP nsCMSDecoder::Update(const char *buf, int32_t len) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSDecoder::Update\n")); michael@0: NSS_CMSDecoder_Update(m_dcx, (char *)buf, len); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void finish (); */ michael@0: NS_IMETHODIMP nsCMSDecoder::Finish(nsICMSMessage ** aCMSMsg) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSDecoder::Finish\n")); michael@0: NSSCMSMessage *cmsMsg; michael@0: cmsMsg = NSS_CMSDecoder_Finish(m_dcx); michael@0: m_dcx = nullptr; michael@0: if (cmsMsg) { michael@0: nsCMSMessage *obj = new nsCMSMessage(cmsMsg); michael@0: // The NSS object cmsMsg still carries a reference to the context michael@0: // we gave it on construction. michael@0: // Make sure the context will live long enough. michael@0: obj->referenceContext(m_ctx); michael@0: *aCMSMsg = obj; michael@0: NS_ADDREF(*aCMSMsg); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCMSEncoder, nsICMSEncoder) michael@0: michael@0: nsCMSEncoder::nsCMSEncoder() michael@0: : m_ecx(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsCMSEncoder::~nsCMSEncoder() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) { michael@0: return; michael@0: } michael@0: destructorSafeDestroyNSSReference(); michael@0: shutdown(calledFromObject); michael@0: } michael@0: michael@0: void nsCMSEncoder::virtualDestroyNSSReference() michael@0: { michael@0: destructorSafeDestroyNSSReference(); michael@0: } michael@0: michael@0: void nsCMSEncoder::destructorSafeDestroyNSSReference() michael@0: { michael@0: if (m_ecx) michael@0: NSS_CMSEncoder_Cancel(m_ecx); michael@0: } michael@0: michael@0: /* void start (); */ michael@0: NS_IMETHODIMP nsCMSEncoder::Start(nsICMSMessage *aMsg, NSSCMSContentCallback cb, void * arg) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Start\n")); michael@0: nsCMSMessage *cmsMsg = static_cast(aMsg); michael@0: m_ctx = new PipUIContext(); michael@0: michael@0: m_ecx = NSS_CMSEncoder_Start(cmsMsg->getCMS(), cb, arg, 0, 0, 0, m_ctx, 0, 0, 0, 0); michael@0: if (!m_ecx) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Start - can't start encoder\n")); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void update (in string aBuf, in long aLen); */ michael@0: NS_IMETHODIMP nsCMSEncoder::Update(const char *aBuf, int32_t aLen) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Update\n")); michael@0: if (!m_ecx || NSS_CMSEncoder_Update(m_ecx, aBuf, aLen) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Update - can't update encoder\n")); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void finish (); */ michael@0: NS_IMETHODIMP nsCMSEncoder::Finish() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: nsresult rv = NS_OK; michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Finish\n")); michael@0: if (!m_ecx || NSS_CMSEncoder_Finish(m_ecx) != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Finish - can't finish encoder\n")); michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: m_ecx = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: /* void encode (in nsICMSMessage aMsg); */ michael@0: NS_IMETHODIMP nsCMSEncoder::Encode(nsICMSMessage *aMsg) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Encode\n")); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: }