summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/extensions/smime/nsCMS.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/extensions/smime/nsCMS.cpp')
-rw-r--r--comm/mailnews/extensions/smime/nsCMS.cpp1187
1 files changed, 1187 insertions, 0 deletions
diff --git a/comm/mailnews/extensions/smime/nsCMS.cpp b/comm/mailnews/extensions/smime/nsCMS.cpp
new file mode 100644
index 0000000000..be743b9663
--- /dev/null
+++ b/comm/mailnews/extensions/smime/nsCMS.cpp
@@ -0,0 +1,1187 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "nsCMS.h"
+
+#include "CertVerifier.h"
+#include "CryptoTask.h"
+#include "ScopedNSSTypes.h"
+#include "cms.h"
+#include "mozilla/Logging.h"
+#include "mozilla/RefPtr.h"
+#include "nsDependentSubstring.h"
+#include "nsICryptoHash.h"
+#include "nsISupports.h"
+#include "nsIX509CertDB.h"
+#include "nsNSSCertificate.h"
+#include "nsNSSComponent.h"
+#include "nsNSSHelper.h"
+#include "nsServiceManagerUtils.h"
+#include "mozpkix/Result.h"
+#include "mozpkix/pkixtypes.h"
+#include "sechash.h"
+#include "secerr.h"
+#include "smime.h"
+#include "mozilla/StaticMutex.h"
+#include "nsIPrefBranch.h"
+
+using namespace mozilla;
+using namespace mozilla::psm;
+using namespace mozilla::pkix;
+
+static mozilla::LazyLogModule gCMSLog("CMS");
+
+NS_IMPL_ISUPPORTS(nsCMSMessage, nsICMSMessage)
+
+nsCMSMessage::nsCMSMessage() { m_cmsMsg = nullptr; }
+nsCMSMessage::nsCMSMessage(NSSCMSMessage* aCMSMsg) { m_cmsMsg = aCMSMsg; }
+
+nsCMSMessage::~nsCMSMessage() {
+ if (m_cmsMsg) {
+ NSS_CMSMessage_Destroy(m_cmsMsg);
+ }
+}
+
+nsresult nsCMSMessage::Init() {
+ nsresult rv;
+ nsCOMPtr<nsISupports> nssInitialized =
+ do_GetService("@mozilla.org/psm;1", &rv);
+ return rv;
+}
+
+NS_IMETHODIMP nsCMSMessage::VerifySignature(int32_t verifyFlags) {
+ return CommonVerifySignature(verifyFlags, {}, 0);
+}
+
+NSSCMSSignerInfo* nsCMSMessage::GetTopLevelSignerInfo() {
+ if (!m_cmsMsg) return nullptr;
+
+ if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) return nullptr;
+
+ NSSCMSContentInfo* cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0);
+ if (!cinfo) return nullptr;
+
+ NSSCMSSignedData* sigd =
+ (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo);
+ if (!sigd) return nullptr;
+
+ PR_ASSERT(NSS_CMSSignedData_SignerInfoCount(sigd) > 0);
+ return NSS_CMSSignedData_GetSignerInfo(sigd, 0);
+}
+
+NS_IMETHODIMP nsCMSMessage::GetSignerEmailAddress(char** aEmail) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::GetSignerEmailAddress"));
+ NS_ENSURE_ARG(aEmail);
+
+ NSSCMSSignerInfo* si = GetTopLevelSignerInfo();
+ if (!si) return NS_ERROR_FAILURE;
+
+ *aEmail = NSS_CMSSignerInfo_GetSignerEmailAddress(si);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsCMSMessage::GetSignerCommonName(char** aName) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::GetSignerCommonName"));
+ NS_ENSURE_ARG(aName);
+
+ NSSCMSSignerInfo* si = GetTopLevelSignerInfo();
+ if (!si) return NS_ERROR_FAILURE;
+
+ *aName = NSS_CMSSignerInfo_GetSignerCommonName(si);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsCMSMessage::ContentIsEncrypted(bool* isEncrypted) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::ContentIsEncrypted"));
+ NS_ENSURE_ARG(isEncrypted);
+
+ if (!m_cmsMsg) return NS_ERROR_FAILURE;
+
+ *isEncrypted = NSS_CMSMessage_IsEncrypted(m_cmsMsg);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsCMSMessage::ContentIsSigned(bool* isSigned) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::ContentIsSigned"));
+ NS_ENSURE_ARG(isSigned);
+
+ if (!m_cmsMsg) return NS_ERROR_FAILURE;
+
+ *isSigned = NSS_CMSMessage_IsSigned(m_cmsMsg);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsCMSMessage::GetSignerCert(nsIX509Cert** scert) {
+ NSSCMSSignerInfo* si = GetTopLevelSignerInfo();
+ if (!si) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIX509Cert> cert;
+ if (si->cert) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::GetSignerCert got signer cert"));
+
+ nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
+ nsTArray<uint8_t> certBytes;
+ certBytes.AppendElements(si->cert->derCert.data, si->cert->derCert.len);
+ nsresult rv = certdb->ConstructX509(certBytes, getter_AddRefs(cert));
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::GetSignerCert no signer cert, do we have a cert "
+ "list? %s",
+ (si->certList ? "yes" : "no")));
+
+ *scert = nullptr;
+ }
+
+ cert.forget(scert);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsCMSMessage::GetEncryptionCert(nsIX509Cert**) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsCMSMessage::GetSigningTime(PRTime* aTime) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::GetSigningTime"));
+ NS_ENSURE_ARG(aTime);
+
+ NSSCMSSignerInfo* si = GetTopLevelSignerInfo();
+ if (!si) {
+ return NS_ERROR_FAILURE;
+ }
+
+ SECStatus getSigningTimeResult = NSS_CMSSignerInfo_GetSigningTime(si, aTime);
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::GetSigningTime result: %s",
+ (getSigningTimeResult ? "ok" : "fail")));
+
+ return getSigningTimeResult == SECSuccess ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsCMSMessage::VerifyDetachedSignature(int32_t verifyFlags,
+ const nsTArray<uint8_t>& aDigestData,
+ int16_t aDigestType) {
+ if (aDigestData.IsEmpty()) return NS_ERROR_FAILURE;
+
+ return CommonVerifySignature(verifyFlags, aDigestData, aDigestType);
+}
+
+// This is an exact copy of NSS_CMSArray_Count from NSS' cmsarray.c,
+// temporarily necessary, see below for for justification.
+static int myNSS_CMSArray_Count(void** array) {
+ int n = 0;
+
+ if (array == NULL) return 0;
+
+ while (*array++ != NULL) n++;
+
+ return n;
+}
+
+// This is an exact copy of NSS_CMSArray_Add from NSS' cmsarray.c,
+// temporarily necessary, see below for for justification.
+static SECStatus myNSS_CMSArray_Add(PLArenaPool* poolp, void*** array,
+ void* obj) {
+ void** p;
+ int n;
+ void** dest;
+
+ PORT_Assert(array != NULL);
+ if (array == NULL) return SECFailure;
+
+ if (*array == NULL) {
+ dest = (void**)PORT_ArenaAlloc(poolp, 2 * sizeof(void*));
+ n = 0;
+ } else {
+ n = 0;
+ p = *array;
+ while (*p++) n++;
+ dest = (void**)PORT_ArenaGrow(poolp, *array, (n + 1) * sizeof(void*),
+ (n + 2) * sizeof(void*));
+ }
+
+ if (dest == NULL) return SECFailure;
+
+ dest[n] = obj;
+ dest[n + 1] = NULL;
+ *array = dest;
+ return SECSuccess;
+}
+
+// This is an exact copy of NSS_CMSArray_Add from NSS' cmsarray.c,
+// temporarily necessary, see below for for justification.
+static SECStatus myNSS_CMSSignedData_AddTempCertificate(NSSCMSSignedData* sigd,
+ CERTCertificate* cert) {
+ CERTCertificate* c;
+ SECStatus rv;
+
+ if (!sigd || !cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ c = CERT_DupCertificate(cert);
+ rv = myNSS_CMSArray_Add(sigd->cmsg->poolp, (void***)&(sigd->tempCerts),
+ (void*)c);
+ return rv;
+}
+
+typedef SECStatus (*extraVerificationOnCertFn)(CERTCertificate* cert,
+ SECCertUsage certusage);
+
+static SECStatus myExtraVerificationOnCert(CERTCertificate* cert,
+ SECCertUsage certusage) {
+ RefPtr<SharedCertVerifier> certVerifier;
+ certVerifier = GetDefaultCertVerifier();
+ if (!certVerifier) {
+ return SECFailure;
+ }
+
+ SECCertificateUsage usageForPkix;
+
+ switch (certusage) {
+ case certUsageEmailSigner:
+ usageForPkix = certificateUsageEmailSigner;
+ break;
+ case certUsageEmailRecipient:
+ usageForPkix = certificateUsageEmailRecipient;
+ break;
+ default:
+ return SECFailure;
+ }
+
+ nsTArray<uint8_t> certBytes(cert->derCert.data, cert->derCert.len);
+ nsTArray<nsTArray<uint8_t>> builtChain;
+ // This code is used when verifying incoming certificates, including
+ // a signature certificate. Performing OCSP is necessary.
+ // Allowing OCSP in blocking mode should be fine, because all our
+ // callers run this code on a separate thread, using
+ // SMimeVerificationTask/CryptoTask.
+ mozilla::pkix::Result result = certVerifier->VerifyCert(
+ certBytes, usageForPkix, Now(), nullptr /*XXX pinarg*/,
+ nullptr /*hostname*/, builtChain);
+ if (result != mozilla::pkix::Success) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+// This is a temporary copy of NSS_CMSSignedData_ImportCerts, which
+// performs additional verifications prior to import.
+// The copy is almost identical to the original.
+//
+// The ONLY DIFFERENCE is the addition of parameter extraVerifyFn,
+// and the call to it - plus a non-null check.
+//
+// NSS should add this or a similar API in the future,
+// and then these temporary functions should be removed, including
+// the ones above. Request is tracked in bugzilla 1738592.
+static SECStatus myNSS_CMSSignedData_ImportCerts(
+ NSSCMSSignedData* sigd, CERTCertDBHandle* certdb, SECCertUsage certusage,
+ PRBool keepcerts, extraVerificationOnCertFn extraVerifyFn) {
+ int certcount;
+ CERTCertificate** certArray = NULL;
+ CERTCertList* certList = NULL;
+ CERTCertListNode* node;
+ SECStatus rv;
+ SECItem** rawArray;
+ int i;
+ PRTime now;
+
+ if (!sigd) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ certcount = myNSS_CMSArray_Count((void**)sigd->rawCerts);
+
+ /* get the certs in the temp DB */
+ rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts,
+ &certArray, PR_FALSE, PR_FALSE, NULL);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* save the certs so they don't get destroyed */
+ for (i = 0; i < certcount; i++) {
+ CERTCertificate* cert = certArray[i];
+ if (cert) myNSS_CMSSignedData_AddTempCertificate(sigd, cert);
+ }
+
+ if (!keepcerts) {
+ goto done;
+ }
+
+ /* build a CertList for filtering */
+ certList = CERT_NewCertList();
+ if (certList == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ for (i = 0; i < certcount; i++) {
+ CERTCertificate* cert = certArray[i];
+ if (cert) cert = CERT_DupCertificate(cert);
+ if (cert) CERT_AddCertToListTail(certList, cert);
+ }
+
+ /* filter out the certs we don't want */
+ rv = CERT_FilterCertListByUsage(certList, certusage, PR_FALSE);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* go down the remaining list of certs and verify that they have
+ * valid chains, then import them.
+ */
+ now = PR_Now();
+ for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
+ node = CERT_LIST_NEXT(node)) {
+ CERTCertificateList* certChain;
+
+ if (!node->cert) {
+ continue;
+ }
+
+ if (extraVerifyFn) {
+ if ((*extraVerifyFn)(node->cert, certusage) != SECSuccess) {
+ continue;
+ }
+ }
+
+ if (CERT_VerifyCert(certdb, node->cert, PR_TRUE, certusage, now, NULL,
+ NULL) != SECSuccess) {
+ continue;
+ }
+
+ certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE);
+ if (!certChain) {
+ continue;
+ }
+
+ /*
+ * CertChain returns an array of SECItems, import expects an array of
+ * SECItem pointers. Create the SECItem Pointers from the array of
+ * SECItems.
+ */
+ rawArray = (SECItem**)PORT_Alloc(certChain->len * sizeof(SECItem*));
+ if (!rawArray) {
+ CERT_DestroyCertificateList(certChain);
+ continue;
+ }
+ for (i = 0; i < certChain->len; i++) {
+ rawArray[i] = &certChain->certs[i];
+ }
+ (void)CERT_ImportCerts(certdb, certusage, certChain->len, rawArray, NULL,
+ keepcerts, PR_FALSE, NULL);
+ PORT_Free(rawArray);
+ CERT_DestroyCertificateList(certChain);
+ }
+
+ rv = SECSuccess;
+
+ /* XXX CRL handling */
+
+done:
+ if (sigd->signerInfos != NULL) {
+ /* fill in all signerinfo's certs */
+ for (i = 0; sigd->signerInfos[i] != NULL; i++)
+ (void)NSS_CMSSignerInfo_GetSigningCertificate(sigd->signerInfos[i],
+ certdb);
+ }
+
+loser:
+ /* now free everything */
+ if (certArray) {
+ CERT_DestroyCertArray(certArray, certcount);
+ }
+ if (certList) {
+ CERT_DestroyCertList(certList);
+ }
+
+ return rv;
+}
+
+nsresult nsCMSMessage::CommonVerifySignature(
+ int32_t verifyFlags, const nsTArray<uint8_t>& aDigestData,
+ int16_t aDigestType) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature, content level count %d",
+ NSS_CMSMessage_ContentLevelCount(m_cmsMsg)));
+ NSSCMSContentInfo* cinfo = nullptr;
+ NSSCMSSignedData* sigd = nullptr;
+ NSSCMSSignerInfo* si;
+ int32_t nsigners;
+ nsresult rv = NS_ERROR_FAILURE;
+ SECOidTag sigAlgTag;
+
+ if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - not signed"));
+ return NS_ERROR_CMS_VERIFY_NOT_SIGNED;
+ }
+
+ cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0);
+ if (cinfo) {
+ switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ sigd = reinterpret_cast<NSSCMSSignedData*>(
+ NSS_CMSContentInfo_GetContent(cinfo));
+ break;
+
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ default: {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - unexpected "
+ "ContentTypeTag"));
+ rv = NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
+ goto loser;
+ }
+ }
+ }
+
+ if (!sigd) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - no content info"));
+ rv = NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
+ goto loser;
+ }
+
+ if (!aDigestData.IsEmpty()) {
+ SECOidTag oidTag;
+ SECItem digest;
+ // NSS_CMSSignedData_SetDigestValue() takes a copy and won't mutate our
+ // data, so we're OK to cast away the const here.
+ digest.data = const_cast<uint8_t*>(aDigestData.Elements());
+ digest.len = aDigestData.Length();
+
+ if (NSS_CMSSignedData_HasDigests(sigd)) {
+ SECAlgorithmID** existingAlgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
+ if (existingAlgs) {
+ while (*existingAlgs) {
+ SECAlgorithmID* alg = *existingAlgs;
+ SECOidTag algOIDTag = SECOID_FindOIDTag(&alg->algorithm);
+ NSS_CMSSignedData_SetDigestValue(sigd, algOIDTag, NULL);
+ ++existingAlgs;
+ }
+ }
+ }
+
+ oidTag =
+ HASH_GetHashOidTagByHashType(static_cast<HASH_HashType>(aDigestType));
+ if (oidTag == SEC_OID_UNKNOWN) {
+ rv = NS_ERROR_CMS_VERIFY_BAD_DIGEST;
+ goto loser;
+ }
+
+ if (NSS_CMSSignedData_SetDigestValue(sigd, oidTag, &digest)) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - bad digest"));
+ rv = NS_ERROR_CMS_VERIFY_BAD_DIGEST;
+ goto loser;
+ }
+ }
+
+ // Import certs. Note that import failure is not a signature verification
+ // failure. //
+ if (myNSS_CMSSignedData_ImportCerts(
+ sigd, CERT_GetDefaultCertDB(), certUsageEmailRecipient, true,
+ myExtraVerificationOnCert) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - can not import certs"));
+ }
+
+ nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
+ PR_ASSERT(nsigners > 0);
+ NS_ENSURE_TRUE(nsigners > 0, NS_ERROR_UNEXPECTED);
+ si = NSS_CMSSignedData_GetSignerInfo(sigd, 0);
+
+ NS_ENSURE_TRUE(si, NS_ERROR_UNEXPECTED);
+ NS_ENSURE_TRUE(si->cert, NS_ERROR_UNEXPECTED);
+
+ // See bug 324474. We want to make sure the signing cert is
+ // still valid at the current time.
+
+ if (myExtraVerificationOnCert(si->cert, certUsageEmailSigner) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - signing cert not trusted "
+ "now"));
+ rv = NS_ERROR_CMS_VERIFY_UNTRUSTED;
+ goto loser;
+ }
+
+ sigAlgTag = NSS_CMSSignerInfo_GetDigestAlgTag(si);
+ switch (sigAlgTag) {
+ case SEC_OID_SHA256:
+ case SEC_OID_SHA384:
+ case SEC_OID_SHA512:
+ break;
+
+ case SEC_OID_SHA1:
+ if (verifyFlags & nsICMSVerifyFlags::VERIFY_ALLOW_WEAK_SHA1) {
+ break;
+ }
+ // else fall through to failure
+#if defined(__clang__)
+ [[clang::fallthrough]];
+#endif
+
+ default:
+ MOZ_LOG(
+ gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - unsupported digest algo"));
+ rv = NS_ERROR_CMS_VERIFY_UNSUPPORTED_ALGO;
+ goto loser;
+ };
+
+ // We verify the first signer info, only //
+ // XXX: NSS_CMSSignedData_VerifySignerInfo calls CERT_VerifyCert, which
+ // requires NSS's certificate verification configuration to be done in
+ // order to work well (e.g. honoring OCSP preferences and proxy settings
+ // for OCSP requests), but Gecko stopped doing that configuration. Something
+ // similar to what was done for Gecko bug 1028643 needs to be done here too.
+ if (NSS_CMSSignedData_VerifySignerInfo(sigd, 0, CERT_GetDefaultCertDB(),
+ certUsageEmailSigner) != SECSuccess) {
+ MOZ_LOG(
+ gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - unable to verify signature"));
+
+ if (NSSCMSVS_SigningCertNotFound == si->verificationStatus) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - signing cert not found"));
+ rv = NS_ERROR_CMS_VERIFY_NOCERT;
+ } else if (NSSCMSVS_SigningCertNotTrusted == si->verificationStatus) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - signing cert not trusted "
+ "at signing time"));
+ rv = NS_ERROR_CMS_VERIFY_UNTRUSTED;
+ } else if (NSSCMSVS_Unverified == si->verificationStatus) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - can not verify"));
+ rv = NS_ERROR_CMS_VERIFY_ERROR_UNVERIFIED;
+ } else if (NSSCMSVS_ProcessingError == si->verificationStatus) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - processing error"));
+ rv = NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
+ } else if (NSSCMSVS_BadSignature == si->verificationStatus) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - bad signature"));
+ rv = NS_ERROR_CMS_VERIFY_BAD_SIGNATURE;
+ } else if (NSSCMSVS_DigestMismatch == si->verificationStatus) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - digest mismatch"));
+ rv = NS_ERROR_CMS_VERIFY_DIGEST_MISMATCH;
+ } else if (NSSCMSVS_SignatureAlgorithmUnknown == si->verificationStatus) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - algo unknown"));
+ rv = NS_ERROR_CMS_VERIFY_UNKNOWN_ALGO;
+ } else if (NSSCMSVS_SignatureAlgorithmUnsupported ==
+ si->verificationStatus) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - algo not supported"));
+ rv = NS_ERROR_CMS_VERIFY_UNSUPPORTED_ALGO;
+ } else if (NSSCMSVS_MalformedSignature == si->verificationStatus) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - malformed signature"));
+ 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) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CommonVerifySignature - unable to save smime "
+ "profile"));
+ }
+
+ rv = NS_OK;
+loser:
+ return rv;
+}
+
+NS_IMETHODIMP nsCMSMessage::AsyncVerifySignature(
+ int32_t verifyFlags, nsISMimeVerificationListener* aListener) {
+ return CommonAsyncVerifySignature(verifyFlags, aListener, {}, 0);
+}
+
+NS_IMETHODIMP nsCMSMessage::AsyncVerifyDetachedSignature(
+ int32_t verifyFlags, nsISMimeVerificationListener* aListener,
+ const nsTArray<uint8_t>& aDigestData, int16_t aDigestType) {
+ if (aDigestData.IsEmpty()) return NS_ERROR_FAILURE;
+
+ return CommonAsyncVerifySignature(verifyFlags, aListener, aDigestData,
+ aDigestType);
+}
+
+class SMimeVerificationTask final : public CryptoTask {
+ public:
+ SMimeVerificationTask(nsICMSMessage* aMessage, int32_t verifyFlags,
+ nsISMimeVerificationListener* aListener,
+ const nsTArray<uint8_t>& aDigestData,
+ int16_t aDigestType)
+ : mMessage(aMessage),
+ mListener(aListener),
+ mDigestData(aDigestData.Clone()),
+ mDigestType(aDigestType),
+ mVerifyFlags(verifyFlags) {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ private:
+ virtual nsresult CalculateResult() override {
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ // Because the S/MIME code and related certificate processing isn't
+ // sufficiently threadsafe (see bug 1529003), we want this code to
+ // never run in parallel (see bug 1386601).
+ mozilla::StaticMutexAutoLock lock(sMutex);
+ nsresult rv;
+ if (mDigestData.IsEmpty()) {
+ rv = mMessage->VerifySignature(mVerifyFlags);
+ } else {
+ rv = mMessage->VerifyDetachedSignature(mVerifyFlags, mDigestData,
+ mDigestType);
+ }
+
+ return rv;
+ }
+ virtual void CallCallback(nsresult rv) override {
+ MOZ_ASSERT(NS_IsMainThread());
+ mListener->Notify(mMessage, rv);
+ }
+
+ nsCOMPtr<nsICMSMessage> mMessage;
+ nsCOMPtr<nsISMimeVerificationListener> mListener;
+ nsTArray<uint8_t> mDigestData;
+ int16_t mDigestType;
+ int32_t mVerifyFlags;
+
+ static mozilla::StaticMutex sMutex;
+};
+
+mozilla::StaticMutex SMimeVerificationTask::sMutex;
+
+nsresult nsCMSMessage::CommonAsyncVerifySignature(
+ int32_t verifyFlags, nsISMimeVerificationListener* aListener,
+ const nsTArray<uint8_t>& aDigestData, int16_t aDigestType) {
+ RefPtr<CryptoTask> task = new SMimeVerificationTask(
+ this, verifyFlags, aListener, aDigestData, aDigestType);
+ return task->Dispatch();
+}
+
+class nsZeroTerminatedCertArray {
+ public:
+ nsZeroTerminatedCertArray() : mCerts(nullptr), mPoolp(nullptr), mSize(0) {}
+
+ ~nsZeroTerminatedCertArray() {
+ if (mCerts) {
+ for (uint32_t i = 0; i < mSize; i++) {
+ if (mCerts[i]) {
+ CERT_DestroyCertificate(mCerts[i]);
+ }
+ }
+ }
+
+ if (mPoolp) PORT_FreeArena(mPoolp, false);
+ }
+
+ bool allocate(uint32_t count) {
+ // only allow allocation once
+ if (mPoolp) return false;
+
+ mSize = count;
+
+ if (!mSize) return false;
+
+ mPoolp = PORT_NewArena(1024);
+ if (!mPoolp) return false;
+
+ mCerts = (CERTCertificate**)PORT_ArenaZAlloc(
+ mPoolp, (count + 1) * sizeof(CERTCertificate*));
+
+ if (!mCerts) return false;
+
+ // null array, including zero termination
+ for (uint32_t i = 0; i < count + 1; i++) {
+ mCerts[i] = nullptr;
+ }
+
+ return true;
+ }
+
+ void set(uint32_t i, CERTCertificate* c) {
+ if (i >= mSize) return;
+
+ if (mCerts[i]) {
+ CERT_DestroyCertificate(mCerts[i]);
+ }
+
+ mCerts[i] = CERT_DupCertificate(c);
+ }
+
+ CERTCertificate* get(uint32_t i) {
+ if (i >= mSize) return nullptr;
+
+ return CERT_DupCertificate(mCerts[i]);
+ }
+
+ CERTCertificate** getRawArray() { return mCerts; }
+
+ private:
+ CERTCertificate** mCerts;
+ PLArenaPool* mPoolp;
+ uint32_t mSize;
+};
+
+NS_IMETHODIMP nsCMSMessage::CreateEncrypted(
+ const nsTArray<RefPtr<nsIX509Cert>>& aRecipientCerts) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::CreateEncrypted"));
+ NSSCMSContentInfo* cinfo;
+ NSSCMSEnvelopedData* envd;
+ NSSCMSRecipientInfo* recipientInfo;
+ nsZeroTerminatedCertArray recipientCerts;
+ SECOidTag bulkAlgTag;
+ int keySize;
+ uint32_t i;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ // Check the recipient certificates //
+ uint32_t recipientCertCount = aRecipientCerts.Length();
+ PR_ASSERT(recipientCertCount > 0);
+
+ if (!recipientCerts.allocate(recipientCertCount)) {
+ goto loser;
+ }
+
+ for (i = 0; i < recipientCertCount; i++) {
+ nsIX509Cert* x509cert = aRecipientCerts[i];
+
+ if (!x509cert) return NS_ERROR_FAILURE;
+
+ UniqueCERTCertificate c(x509cert->GetCert());
+ recipientCerts.set(i, c.get());
+ }
+
+ // Find a bulk key algorithm //
+ if (NSS_SMIMEUtil_FindBulkAlgForRecipients(
+ recipientCerts.getRawArray(), &bulkAlgTag, &keySize) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateEncrypted - can't find bulk alg for "
+ "recipients"));
+ rv = NS_ERROR_CMS_ENCRYPT_NO_BULK_ALG;
+ goto loser;
+ }
+
+ m_cmsMsg = NSS_CMSMessage_Create(nullptr);
+ if (!m_cmsMsg) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateEncrypted - can't create new cms message"));
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto loser;
+ }
+
+ if ((envd = NSS_CMSEnvelopedData_Create(m_cmsMsg, bulkAlgTag, keySize)) ==
+ nullptr) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateEncrypted - can't create enveloped data"));
+ goto loser;
+ }
+
+ cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg);
+ if (NSS_CMSContentInfo_SetContent_EnvelopedData(m_cmsMsg, cinfo, envd) !=
+ SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateEncrypted - can't create content enveloped "
+ "data"));
+ goto loser;
+ }
+
+ cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd);
+ if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nullptr, false) !=
+ SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateEncrypted - can't set content data"));
+ goto loser;
+ }
+
+ // Create and attach recipient information //
+ for (i = 0; i < recipientCertCount; i++) {
+ UniqueCERTCertificate rc(recipientCerts.get(i));
+ if ((recipientInfo = NSS_CMSRecipientInfo_Create(m_cmsMsg, rc.get())) ==
+ nullptr) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateEncrypted - can't create recipient info"));
+ goto loser;
+ }
+ if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientInfo) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateEncrypted - can't add recipient info"));
+ goto loser;
+ }
+ }
+
+ return NS_OK;
+loser:
+ if (m_cmsMsg) {
+ NSS_CMSMessage_Destroy(m_cmsMsg);
+ m_cmsMsg = nullptr;
+ }
+
+ return rv;
+}
+
+bool nsCMSMessage::IsAllowedHash(const int16_t aCryptoHashInt) {
+ switch (aCryptoHashInt) {
+ case nsICryptoHash::SHA1:
+ case nsICryptoHash::SHA256:
+ case nsICryptoHash::SHA384:
+ case nsICryptoHash::SHA512:
+ return true;
+ default:
+ return false;
+ }
+}
+
+NS_IMETHODIMP
+nsCMSMessage::CreateSigned(nsIX509Cert* aSigningCert, nsIX509Cert* aEncryptCert,
+ const nsTArray<uint8_t>& aDigestData,
+ int16_t aDigestType) {
+ NS_ENSURE_ARG(aSigningCert);
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSMessage::CreateSigned"));
+ NSSCMSContentInfo* cinfo;
+ NSSCMSSignedData* sigd;
+ NSSCMSSignerInfo* signerinfo;
+ UniqueCERTCertificate scert(aSigningCert->GetCert());
+ UniqueCERTCertificate ecert;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (!scert) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aEncryptCert) {
+ ecert = UniqueCERTCertificate(aEncryptCert->GetCert());
+ }
+
+ if (!IsAllowedHash(aDigestType)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ SECOidTag digestType =
+ HASH_GetHashOidTagByHashType(static_cast<HASH_HashType>(aDigestType));
+ if (digestType == SEC_OID_UNKNOWN) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ /*
+ * create the message object
+ */
+ m_cmsMsg =
+ NSS_CMSMessage_Create(nullptr); /* create a message on its own pool */
+ if (!m_cmsMsg) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't create new message"));
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto loser;
+ }
+
+ /*
+ * build chain of objects: message->signedData->data
+ */
+ if ((sigd = NSS_CMSSignedData_Create(m_cmsMsg)) == nullptr) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't create signed data"));
+ goto loser;
+ }
+ cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg);
+ if (NSS_CMSContentInfo_SetContent_SignedData(m_cmsMsg, cinfo, sigd) !=
+ SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't set content signed data"));
+ goto loser;
+ }
+
+ cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
+
+ /* we're always passing data in and detaching optionally */
+ if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nullptr, true) !=
+ SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't set content data"));
+ goto loser;
+ }
+
+ /*
+ * create & attach signer information
+ */
+ signerinfo = NSS_CMSSignerInfo_Create(m_cmsMsg, scert.get(), digestType);
+ if (!signerinfo) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't create signer info"));
+ goto loser;
+ }
+
+ /* we want the cert chain included for this one */
+ if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain,
+ certUsageEmailSigner) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't include signer cert chain"));
+ goto loser;
+ }
+
+ if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't add signing time"));
+ goto loser;
+ }
+
+ if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't add smime caps"));
+ goto loser;
+ }
+
+ if (ecert) {
+ if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(
+ signerinfo, ecert.get(), CERT_GetDefaultCertDB()) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't add smime enc key prefs"));
+ goto loser;
+ }
+
+ if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(
+ signerinfo, ecert.get(), CERT_GetDefaultCertDB()) != SECSuccess) {
+ MOZ_LOG(
+ gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't add MS smime enc key prefs"));
+ goto loser;
+ }
+
+ // If signing and encryption cert are identical, don't add it twice.
+ bool addEncryptionCert =
+ (ecert && (!scert || !CERT_CompareCerts(ecert.get(), scert.get())));
+
+ if (addEncryptionCert &&
+ NSS_CMSSignedData_AddCertificate(sigd, ecert.get()) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't add own encryption "
+ "certificate"));
+ goto loser;
+ }
+ }
+
+ if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't add signer info"));
+ goto loser;
+ }
+
+ // Finally, add the pre-computed digest if passed in
+ if (!aDigestData.IsEmpty()) {
+ SECItem digest;
+
+ // NSS_CMSSignedData_SetDigestValue() takes a copy and won't mutate our
+ // data, so we're OK to cast away the const here.
+ digest.data = const_cast<uint8_t*>(aDigestData.Elements());
+ digest.len = aDigestData.Length();
+ if (NSS_CMSSignedData_SetDigestValue(sigd, digestType, &digest) !=
+ SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSMessage::CreateSigned - can't set digest value"));
+ goto loser;
+ }
+ }
+
+ return NS_OK;
+loser:
+ if (m_cmsMsg) {
+ NSS_CMSMessage_Destroy(m_cmsMsg);
+ m_cmsMsg = nullptr;
+ }
+ return rv;
+}
+
+NS_IMPL_ISUPPORTS(nsCMSDecoder, nsICMSDecoder)
+NS_IMPL_ISUPPORTS(nsCMSDecoderJS, nsICMSDecoderJS)
+
+nsCMSDecoder::nsCMSDecoder() : m_dcx(nullptr) {}
+nsCMSDecoderJS::nsCMSDecoderJS() : m_dcx(nullptr) {}
+
+nsCMSDecoder::~nsCMSDecoder() {
+ if (m_dcx) {
+ NSS_CMSDecoder_Cancel(m_dcx);
+ m_dcx = nullptr;
+ }
+}
+
+nsCMSDecoderJS::~nsCMSDecoderJS() {
+ if (m_dcx) {
+ NSS_CMSDecoder_Cancel(m_dcx);
+ m_dcx = nullptr;
+ }
+}
+
+nsresult nsCMSDecoder::Init() {
+ nsresult rv;
+ nsCOMPtr<nsISupports> nssInitialized =
+ do_GetService("@mozilla.org/psm;1", &rv);
+ return rv;
+}
+
+nsresult nsCMSDecoderJS::Init() {
+ nsresult rv;
+ nsCOMPtr<nsISupports> nssInitialized =
+ do_GetService("@mozilla.org/psm;1", &rv);
+ return rv;
+}
+
+/* void start (in NSSCMSContentCallback cb, in voidPtr arg); */
+NS_IMETHODIMP nsCMSDecoder::Start(NSSCMSContentCallback cb, void* arg) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSDecoder::Start"));
+ m_ctx = new PipUIContext();
+
+ m_dcx = NSS_CMSDecoder_Start(0, cb, arg, 0, m_ctx, 0, 0);
+ if (!m_dcx) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSDecoder::Start - can't start decoder"));
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+/* void update (in string bug, in long len); */
+NS_IMETHODIMP nsCMSDecoder::Update(const char* buf, int32_t len) {
+ NSS_CMSDecoder_Update(m_dcx, (char*)buf, len);
+ return NS_OK;
+}
+
+/* void finish (); */
+NS_IMETHODIMP nsCMSDecoder::Finish(nsICMSMessage** aCMSMsg) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSDecoder::Finish"));
+ NSSCMSMessage* cmsMsg;
+ cmsMsg = NSS_CMSDecoder_Finish(m_dcx);
+ m_dcx = nullptr;
+ if (cmsMsg) {
+ nsCMSMessage* obj = new nsCMSMessage(cmsMsg);
+ // The NSS object cmsMsg still carries a reference to the context
+ // we gave it on construction.
+ // Make sure the context will live long enough.
+ obj->referenceContext(m_ctx);
+ NS_ADDREF(*aCMSMsg = obj);
+ }
+ return NS_OK;
+}
+
+void nsCMSDecoderJS::content_callback(void* arg, const char* input,
+ unsigned long length) {
+ nsCMSDecoderJS* self = reinterpret_cast<nsCMSDecoderJS*>(arg);
+ self->mDecryptedData.AppendElements(input, length);
+}
+
+NS_IMETHODIMP nsCMSDecoderJS::Decrypt(const nsTArray<uint8_t>& aInput,
+ nsTArray<uint8_t>& _retval) {
+ if (aInput.IsEmpty()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ m_ctx = new PipUIContext();
+
+ m_dcx = NSS_CMSDecoder_Start(0, nsCMSDecoderJS::content_callback, this, 0,
+ m_ctx, 0, 0);
+ if (!m_dcx) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSDecoderJS::Start - can't start decoder"));
+ return NS_ERROR_FAILURE;
+ }
+
+ NSS_CMSDecoder_Update(m_dcx, (char*)aInput.Elements(), aInput.Length());
+
+ NSSCMSMessage* cmsMsg;
+ cmsMsg = NSS_CMSDecoder_Finish(m_dcx);
+ m_dcx = nullptr;
+ if (cmsMsg) {
+ nsCMSMessage* obj = new nsCMSMessage(cmsMsg);
+ // The NSS object cmsMsg still carries a reference to the context
+ // we gave it on construction.
+ // Make sure the context will live long enough.
+ obj->referenceContext(m_ctx);
+ mCMSMessage = obj;
+ }
+
+ _retval = mDecryptedData.Clone();
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsCMSEncoder, nsICMSEncoder)
+
+nsCMSEncoder::nsCMSEncoder() : m_ecx(nullptr) {}
+
+nsCMSEncoder::~nsCMSEncoder() {
+ if (m_ecx) NSS_CMSEncoder_Cancel(m_ecx);
+}
+
+nsresult nsCMSEncoder::Init() {
+ nsresult rv;
+ nsCOMPtr<nsISupports> nssInitialized =
+ do_GetService("@mozilla.org/psm;1", &rv);
+ return rv;
+}
+
+/* void start (); */
+NS_IMETHODIMP nsCMSEncoder::Start(nsICMSMessage* aMsg, NSSCMSContentCallback cb,
+ void* arg) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSEncoder::Start"));
+ nsCMSMessage* cmsMsg = static_cast<nsCMSMessage*>(aMsg);
+ m_ctx = new PipUIContext();
+
+ m_ecx = NSS_CMSEncoder_Start(cmsMsg->getCMS(), cb, arg, 0, 0, 0, m_ctx, 0, 0,
+ 0, 0);
+ if (!m_ecx) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSEncoder::Start - can't start encoder"));
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+/* void update (in string aBuf, in long aLen); */
+NS_IMETHODIMP nsCMSEncoder::Update(const char* aBuf, int32_t aLen) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSEncoder::Update"));
+ if (!m_ecx || NSS_CMSEncoder_Update(m_ecx, aBuf, aLen) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSEncoder::Update - can't update encoder"));
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+/* void finish (); */
+NS_IMETHODIMP nsCMSEncoder::Finish() {
+ nsresult rv = NS_OK;
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSEncoder::Finish"));
+ if (!m_ecx || NSS_CMSEncoder_Finish(m_ecx) != SECSuccess) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug,
+ ("nsCMSEncoder::Finish - can't finish encoder"));
+ rv = NS_ERROR_FAILURE;
+ }
+ m_ecx = nullptr;
+ return rv;
+}
+
+/* void encode (in nsICMSMessage aMsg); */
+NS_IMETHODIMP nsCMSEncoder::Encode(nsICMSMessage* aMsg) {
+ MOZ_LOG(gCMSLog, LogLevel::Debug, ("nsCMSEncoder::Encode"));
+ return NS_ERROR_NOT_IMPLEMENTED;
+}