/* 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 "cms.h" #include "nsNSSCertificateDB.h" #include "nsNSSCertificate.h" #include "AppSignatureVerification.h" #include "CryptoTask.h" #include "mozpkix/pkix.h" #include "mozpkix/pkixnss.h" #include "mozpkix/pkixtypes.h" #include "mozpkix/pkixutil.h" #include "mozilla/dom/Promise.h" using namespace mozilla; using namespace mozilla::pkix; using namespace mozilla::psm; using dom::Promise; nsresult ComputeDigest(SECOidTag digestAlgorithm, const nsTArray>& ins, /* out */ nsTArray& calculatedDigest) { Digest digest; nsresult rv = digest.Begin(digestAlgorithm); if (NS_FAILED(rv)) { return rv; } for (auto& in : ins) { rv = digest.Update(in.Elements(), in.Length()); if (NS_FAILED(rv)) { return rv; } } return digest.End(calculatedDigest); } struct VerifySignatureResult { nsresult signatureVerificationResult; nsTArray signerCert; Time time; VerifySignatureResult(nsresult result, Span certSpan, Time t) : signatureVerificationResult(result), time(t) { signerCert.AppendElements(certSpan.data(), certSpan.Length()); } }; void VerifySignature( NSSCMSSignedData* signedData, const nsTArray>& data, /* out */ nsTArray& signatureVerificationResults) { nsTArray> signerInfos; // Returns a prioritized list of signerInfos. GetAllSignerInfosForSupportedDigestAlgorithms(signedData, signerInfos); Span signerCertSpan; if (signerInfos.Length() == 0) { signatureVerificationResults.AppendElement( VerifySignatureResult(NS_ERROR_CMS_VERIFY_NOT_SIGNED, /* no certificate */ signerCertSpan, /* default time */ Time(Time::uninitialized))); return; } nsTArray> collectedCerts; CollectCertificates(signedData, collectedCerts); if (collectedCerts.Length() == 0) { signatureVerificationResults.AppendElement( VerifySignatureResult(NS_ERROR_CMS_VERIFY_NOCERT, /* no certificate */ signerCertSpan, /* default time */ Time(Time::uninitialized))); return; } for (const auto& pair : signerInfos) { Time defaultTime(Time::uninitialized); SECOidTag digestAlgorithm = std::get<1>(pair); signerCertSpan = Span(); nsTArray calculatedDigest; nsresult rv = ComputeDigest(digestAlgorithm, data, calculatedDigest); if (NS_FAILED(rv)) { signatureVerificationResults.AppendElement(VerifySignatureResult( rv, /* no certificate */ signerCertSpan, defaultTime)); continue; } NSSCMSSignerInfo* signerInfo = std::get<0>(pair); signerCertSpan = GetPKCS7SignerCert(signerInfo, collectedCerts); if (signerCertSpan.IsEmpty()) { signatureVerificationResults.AppendElement(VerifySignatureResult( NS_ERROR_CMS_VERIFY_NOCERT, /* no certificate */ signerCertSpan, defaultTime)); continue; } SECItem detachedDigest = { siBuffer, calculatedDigest.Elements(), static_cast(calculatedDigest.Length())}; rv = VerifySignatureFromCertificate(signerCertSpan, signerInfo, &detachedDigest); PRTime signingTime; if (NSS_CMSSignerInfo_GetSigningTime(signerInfo, &signingTime) == SECSuccess) { signatureVerificationResults.AppendElement(VerifySignatureResult( rv, signerCertSpan, TimeFromEpochInSeconds((uint64_t)(signingTime / 1000000)))); } else { signatureVerificationResults.AppendElement( VerifySignatureResult(rv, signerCertSpan, defaultTime)); } } } nsresult VerifyCertificate() { return NS_ERROR_CMS_VERIFY_NOT_YET_ATTEMPTED; } class PDFVerificationResultImpl final : public nsIPDFVerificationResult { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIPDFVERIFICATIONRESULT PDFVerificationResultImpl(nsresult sigResult, nsresult certResult, nsIX509Cert* cert) : mSignatureResult(sigResult), mCertificateResult(certResult), mSignerCertificate(cert) {} private: ~PDFVerificationResultImpl() = default; nsresult mSignatureResult; nsresult mCertificateResult; nsCOMPtr mSignerCertificate; }; NS_IMPL_ISUPPORTS(PDFVerificationResultImpl, nsIPDFVerificationResult) NS_IMETHODIMP PDFVerificationResultImpl::GetSignatureResult(nsresult* aResult) { *aResult = mSignatureResult; return NS_OK; } NS_IMETHODIMP PDFVerificationResultImpl::GetCertificateResult(nsresult* aResult) { *aResult = mCertificateResult; return NS_OK; } NS_IMETHODIMP PDFVerificationResultImpl::GetSignerCertificate(nsIX509Cert** aCert) { NS_IF_ADDREF(*aCert = mSignerCertificate); return NS_OK; } void VerifyPKCS7Object( const nsTArray& pkcs7, const nsTArray>& data, nsIX509CertDB::PDFSignatureAlgorithm signatureType, /* out */ nsTArray>& pdfVerifResults) { if (signatureType != nsIX509CertDB::PDFSignatureAlgorithm::ADBE_PKCS7_DETACHED) { pdfVerifResults.AppendElement(new PDFVerificationResultImpl( NS_ERROR_CMS_VERIFY_ERROR_PROCESSING, NS_ERROR_CMS_VERIFY_ERROR_PROCESSING, nullptr)); return; } if (pkcs7.Length() == 0 || data.Length() == 0) { pdfVerifResults.AppendElement(new PDFVerificationResultImpl( NS_ERROR_CMS_VERIFY_ERROR_PROCESSING, NS_ERROR_CMS_VERIFY_ERROR_PROCESSING, nullptr)); return; } SECItem certificateItem = {siBuffer, const_cast(pkcs7.Elements()), static_cast(pkcs7.Length())}; UniqueNSSCMSMessage cmsMsg( NSS_CMSMessage_CreateFromDER(&certificateItem, /* NSSCMSContentCallback */ nullptr, /* cb_arg */ nullptr, /* PK11PasswordFunc */ nullptr, /* pwfn_args */ nullptr, /* NSSCMSGetDecryptKeyCallback */ nullptr, /* decrypt_key_cb_arg */ nullptr)); NSSCMSSignedData* signedData = GetSignedDataContent(cmsMsg.get()); if (!signedData) { pdfVerifResults.AppendElement(new PDFVerificationResultImpl( NS_ERROR_CMS_VERIFY_ERROR_PROCESSING, NS_ERROR_CMS_VERIFY_ERROR_PROCESSING, nullptr)); return; } nsTArray signatureVerificationResults; VerifySignature(signedData, data, signatureVerificationResults); for (auto& result : signatureVerificationResults) { if (result.signatureVerificationResult != NS_OK) { pdfVerifResults.AppendElement(new PDFVerificationResultImpl( result.signatureVerificationResult, NS_ERROR_CMS_VERIFY_NOT_YET_ATTEMPTED, nullptr)); } else { // The next patch will contain the certificate verification for each // signerCert nsCOMPtr cert( new nsNSSCertificate(std::move(result.signerCert))); pdfVerifResults.AppendElement(new PDFVerificationResultImpl( result.signatureVerificationResult, NS_ERROR_CMS_VERIFY_NOT_YET_ATTEMPTED, cert)); } } } class VerifyPKCS7ObjectTask : public CryptoTask { public: VerifyPKCS7ObjectTask(const nsTArray& aPkcs7, const nsTArray>& aData, nsIX509CertDB::PDFSignatureAlgorithm aSignatureType, RefPtr& aPromise) : mPkcs7(aPkcs7.Clone()), mSignatureType(aSignatureType), mPromise(new nsMainThreadPtrHolder( "VerifyPKCS7ObjectTask::mPromise", aPromise)) { for (auto& n : aData) { mData.AppendElement(n.Clone()); } } private: virtual nsresult CalculateResult() override; virtual void CallCallback(nsresult rv) override; const nsTArray mPkcs7; nsTArray> mData; nsIX509CertDB::PDFSignatureAlgorithm mSignatureType; nsTArray> mPdfVerifResults; // out nsMainThreadPtrHandle mPromise; }; NS_IMETHODIMP nsNSSCertificateDB::AsyncVerifyPKCS7Object( const nsTArray& pkcs7, const nsTArray>& data, nsIX509CertDB::PDFSignatureAlgorithm signatureType, JSContext* aCx, mozilla::dom::Promise** aPromise) { NS_ENSURE_ARG_POINTER(aCx); nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx); if (NS_WARN_IF(!globalObject)) { return NS_ERROR_UNEXPECTED; } ErrorResult result; RefPtr promise = Promise::Create(globalObject, result); if (NS_WARN_IF(result.Failed())) { return result.StealNSResult(); } RefPtr task( new VerifyPKCS7ObjectTask(pkcs7, data, signatureType, promise)); nsresult rv = task->Dispatch(); if (NS_FAILED(rv)) { return rv; } promise.forget(aPromise); return NS_OK; } nsresult VerifyPKCS7ObjectTask::CalculateResult() { VerifyPKCS7Object(mPkcs7, mData, mSignatureType, mPdfVerifResults); return NS_OK; } void VerifyPKCS7ObjectTask::CallCallback(nsresult rv) { if (NS_FAILED(rv)) { mPromise->MaybeReject(rv); } else { mPromise->MaybeResolve(mPdfVerifResults); } }