NS_IMETHODIMP nsDataSignatureVerifier::VerifySignature(const char* aRSABuf, uint32_t aRSABufLen, const char* aPlaintext, uint32_t aPlaintextLen, int32_t* aErrorCode, nsICertificatePrincipal** aPrincipal) { if (!aPlaintext || !aPrincipal || !aErrorCode) { return NS_ERROR_INVALID_ARG; } *aErrorCode = VERIFY_ERROR_OTHER; *aPrincipal = nullptr; nsNSSShutDownPreventionLock locker; Digest digest; nsresult rv = digest.DigestBuf(SEC_OID_SHA1, reinterpret_cast<const uint8_t*>(aPlaintext), aPlaintextLen); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } SECItem buffer = { siBuffer, reinterpret_cast<uint8_t*>(const_cast<char*>(aRSABuf)), aRSABufLen }; VerifyCertificateContext context; // XXX: pinArg is missing rv = VerifyCMSDetachedSignatureIncludingCertificate(buffer, digest.get(), VerifyCertificate, &context, nullptr); if (NS_SUCCEEDED(rv)) { *aErrorCode = VERIFY_OK; } else if (NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_SECURITY) { if (rv == GetXPCOMFromNSSError(SEC_ERROR_UNKNOWN_ISSUER)) { *aErrorCode = VERIFY_ERROR_UNKNOWN_ISSUER; } else { *aErrorCode = VERIFY_ERROR_OTHER; } rv = NS_OK; } if (rv == NS_OK) { context.principal.forget(aPrincipal); } return rv; }
NS_IMETHODIMP nsNSSCertificate::GetSha256SubjectPublicKeyInfoDigest(nsACString& aSha256SPKIDigest) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } aSha256SPKIDigest.Truncate(); Digest digest; nsresult rv = digest.DigestBuf(SEC_OID_SHA256, mCert->derPublicKey.data, mCert->derPublicKey.len); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = Base64Encode(nsDependentCSubstring( reinterpret_cast<const char*> (digest.get().data), digest.get().len), aSha256SPKIDigest); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; }
// Called on the worker thread. bool BackgroundFileSaver::CheckCompletion() { nsresult rv; MOZ_ASSERT(!mAsyncCopyContext, "Should not be copying when checking completion conditions."); bool failed = true; { MutexAutoLock lock(mLock); if (mComplete) { return true; } // If an error occurred, we don't need to do the checks in this code block, // and the operation can be completed immediately with a failure code. if (NS_SUCCEEDED(mStatus)) { failed = false; // We did not incur in an error, so we must determine if we can stop now. // If the Finish method has not been called, we can just continue now. if (!mFinishRequested) { return false; } // We can only stop when all the operations requested by the control // thread have been processed. First, we check whether we have processed // the first SetTarget call, if any. Then, we check whether we have // processed any rename requested by subsequent SetTarget calls. if ((mInitialTarget && !mActualTarget) || (mRenamedTarget && mRenamedTarget != mActualTarget)) { return false; } // If we still have data to write to the output file, allow the copy // operation to resume. The Available getter may return an error if one // of the pipe's streams has been already closed. uint64_t available; rv = mPipeInputStream->Available(&available); if (NS_SUCCEEDED(rv) && available != 0) { return false; } } mComplete = true; } // Ensure we notify completion now that the operation finished. // Do a best-effort attempt to remove the file if required. if (failed && mActualTarget && !mActualTargetKeepPartial) { (void)mActualTarget->Remove(false); } // Finish computing the hash if (!failed && mDigestContext) { nsNSSShutDownPreventionLock lock; if (!isAlreadyShutDown()) { Digest d; rv = d.End(SEC_OID_SHA256, mDigestContext); if (NS_SUCCEEDED(rv)) { MutexAutoLock lock(mLock); mSha256 = nsDependentCSubstring(char_ptr_cast(d.get().data), d.get().len); } } } // Compute the signature of the binary. ExtractSignatureInfo doesn't do // anything on non-Windows platforms except return an empty nsIArray. if (!failed && mActualTarget) { nsString filePath; mActualTarget->GetTarget(filePath); nsresult rv = ExtractSignatureInfo(filePath); if (NS_FAILED(rv)) { LOG(("Unable to extract signature information [this = %p].", this)); } else { LOG(("Signature extraction success! [this = %p]", this)); } } // Post an event to notify that the operation completed. if (NS_FAILED(mControlThread->Dispatch(NewRunnableMethod(this, &BackgroundFileSaver::NotifySaveComplete), NS_DISPATCH_NORMAL))) { NS_WARNING("Unable to post completion event to the control thread."); } return true; }
/* * Verify the signature of a directory structure as if it were a * signed JAR file (used for unpacked JARs) */ nsresult VerifySignedDirectory(AppTrustedRoot aTrustedRoot, nsIFile* aDirectory, /*out, optional */ nsIX509Cert** aSignerCert) { NS_ENSURE_ARG_POINTER(aDirectory); if (aSignerCert) { *aSignerCert = nullptr; } // Make sure there's a META-INF directory nsCOMPtr<nsIFile> metaDir; nsresult rv = aDirectory->Clone(getter_AddRefs(metaDir)); if (NS_FAILED(rv)) { return rv; } rv = metaDir->Append(NS_LITERAL_STRING(JAR_META_DIR)); if (NS_FAILED(rv)) { return rv; } bool exists; rv = metaDir->Exists(&exists); if (NS_FAILED(rv) || !exists) { return NS_ERROR_SIGNED_JAR_NOT_SIGNED; } bool isDirectory; rv = metaDir->IsDirectory(&isDirectory); if (NS_FAILED(rv) || !isDirectory) { return NS_ERROR_SIGNED_JAR_NOT_SIGNED; } // Find and load the Signature (RSA) file nsAutoString sigFilename; rv = FindSignatureFilename(metaDir, sigFilename); if (NS_FAILED(rv)) { return rv; } ScopedAutoSECItem sigBuffer; rv = LoadOneMetafile(metaDir, sigFilename, sigBuffer, nullptr); if (NS_FAILED(rv)) { return NS_ERROR_SIGNED_JAR_NOT_SIGNED; } // Load the signature (SF) file and verify the signature. // The .sf and .rsa files must have the same name apart from the extension. nsAutoString sfFilename(Substring(sigFilename, 0, sigFilename.Length() - 3) + NS_LITERAL_STRING("sf")); ScopedAutoSECItem sfBuffer; Digest sfCalculatedDigest; rv = LoadOneMetafile(metaDir, sfFilename, sfBuffer, &sfCalculatedDigest); if (NS_FAILED(rv)) { return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; } sigBuffer.type = siBuffer; ScopedCERTCertList builtChain; rv = VerifySignature(aTrustedRoot, sigBuffer, sfCalculatedDigest.get(), builtChain); if (NS_FAILED(rv)) { return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; } // Get the expected manifest hash from the signed .sf file ScopedAutoSECItem mfDigest; rv = ParseSF(char_ptr_cast(sfBuffer.data), mfDigest); if (NS_FAILED(rv)) { return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; } // Load manifest (MF) file and verify signature nsAutoString mfFilename(NS_LITERAL_STRING("manifest.mf")); ScopedAutoSECItem manifestBuffer; Digest mfCalculatedDigest; rv = LoadOneMetafile(metaDir, mfFilename, manifestBuffer, &mfCalculatedDigest); if (NS_FAILED(rv)) { return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; } if (SECITEM_CompareItem(&mfDigest, &mfCalculatedDigest.get()) != SECEqual) { return NS_ERROR_SIGNED_JAR_MANIFEST_INVALID; } // Parse manifest and verify signed hash of all listed files // Allocate the I/O buffer only once per JAR, instead of once per entry, in // order to minimize malloc/free calls and in order to avoid fragmenting // memory. ScopedAutoSECItem buf(128 * 1024); nsTHashtable<nsStringHashKey> items; rv = ParseMFUnpacked(char_ptr_cast(manifestBuffer.data), aDirectory, items, buf); if (NS_FAILED(rv)){ return rv; } // We've checked that everything listed in the manifest exists and is signed // correctly. Now check on disk for extra (unsigned) files. // Deletes found entries from items as it goes. rv = CheckDirForUnsignedFiles(aDirectory, EmptyString(), items, sigFilename, sfFilename, mfFilename); if (NS_FAILED(rv)) { return rv; } // We verified that every entry that we require to be signed is signed. But, // were there any missing entries--that is, entries that are mentioned in the // manifest but missing from the directory tree? (There shouldn't be given // ParseMFUnpacked() checking them all, but it's a cheap sanity check.) if (items.Count() != 0) { return NS_ERROR_SIGNED_JAR_ENTRY_MISSING; } // Return the signer's certificate to the reader if they want it. // XXX: We should return an nsIX509CertList with the whole validated chain. if (aSignerCert) { MOZ_ASSERT(CERT_LIST_HEAD(builtChain)); nsCOMPtr<nsIX509Cert> signerCert = nsNSSCertificate::Create(CERT_LIST_HEAD(builtChain)->cert); NS_ENSURE_TRUE(signerCert, NS_ERROR_OUT_OF_MEMORY); signerCert.forget(aSignerCert); } return NS_OK; }
// Called on the worker thread. bool BackgroundFileSaver::CheckCompletion() { nsresult rv; MOZ_ASSERT(!mAsyncCopyContext, "Should not be copying when checking completion conditions."); bool failed = true; { MutexAutoLock lock(mLock); if (mComplete) { return true; } // If an error occurred, we don't need to do the checks in this code block, // and the operation can be completed immediately with a failure code. if (NS_SUCCEEDED(mStatus)) { failed = false; // On success, if there is a pending rename operation, we must process it // before finishing. Otherwise, we can finish now if requested. if ((mAssignedTarget && mAssignedTarget != mActualTarget) || !mFinishRequested) { return false; } // If completion was requested, but we still have data to write to the // output file, allow the copy operation to resume. The Available getter // may return an error if one of the pipe's streams has been already closed. uint64_t available; rv = mPipeInputStream->Available(&available); if (NS_SUCCEEDED(rv) && available != 0) { return false; } } mComplete = true; } // Ensure we notify completion now that the operation finished. // Do a best-effort attempt to remove the file if required. if (failed && mActualTarget && !mActualTargetKeepPartial) { (void)mActualTarget->Remove(false); } // Finish computing the hash if (!failed && mDigestContext) { nsNSSShutDownPreventionLock lock; if (!isAlreadyShutDown()) { Digest d; rv = d.End(SEC_OID_SHA256, mDigestContext); if (NS_SUCCEEDED(rv)) { MutexAutoLock lock(mLock); mSha256 = nsDependentCSubstring(char_ptr_cast(d.get().data), d.get().len); } } } // Post an event to notify that the operation completed. nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &BackgroundFileSaver::NotifySaveComplete); if (!event || NS_FAILED(mControlThread->Dispatch(event, NS_DISPATCH_NORMAL))) { NS_WARNING("Unable to post completion event to the control thread."); } return true; }