зеркало из https://github.com/mozilla/gecko-dev.git
Bug 804663: Create a CryptoTask API to simplify the creation of correct async crypto operations and add more utilities to ScopedNSSTypes.h, r=honzab
--HG-- extra : rebase_source : de29f383e6d5b109a06837b887d96bbcbce55817
This commit is contained in:
Родитель
b958ad747b
Коммит
ddd80afae9
|
@ -0,0 +1,71 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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 "CryptoTask.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
CryptoTask::~CryptoTask()
|
||||
{
|
||||
MOZ_ASSERT(mReleasedNSSResources);
|
||||
|
||||
nsNSSShutDownPreventionLock lock;
|
||||
if (!isAlreadyShutDown()) {
|
||||
shutdown(calledFromObject);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
CryptoTask::Dispatch(const nsACString & taskThreadName)
|
||||
{
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
nsresult rv = NS_NewThread(getter_AddRefs(thread), this);
|
||||
if (thread) {
|
||||
NS_SetThreadName(thread, taskThreadName);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CryptoTask::Run()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
mRv = NS_ERROR_NOT_AVAILABLE;
|
||||
} else {
|
||||
mRv = CalculateResult();
|
||||
}
|
||||
NS_DispatchToMainThread(this);
|
||||
} else {
|
||||
// back on the main thread
|
||||
|
||||
// call ReleaseNSSResources now, before calling CallCallback, so that
|
||||
// CryptoTasks have consistent behavior regardless of whether NSS is shut
|
||||
// down between CalculateResult being called and CallCallback being called.
|
||||
if (!mReleasedNSSResources) {
|
||||
mReleasedNSSResources = true;
|
||||
ReleaseNSSResources();
|
||||
}
|
||||
|
||||
CallCallback(mRv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CryptoTask::virtualDestroyNSSReference()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(NS_IsMainThread(),
|
||||
"virtualDestroyNSSReference called off the main thread");
|
||||
if (!mReleasedNSSResources) {
|
||||
mReleasedNSSResources = true;
|
||||
ReleaseNSSResources();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,87 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla__CryptoTask_h
|
||||
#define mozilla__CryptoTask_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* Frequently we need to run a task on a background thread without blocking
|
||||
* the main thread, and then call a callback on the main thread with the
|
||||
* result. This class provides the framework for that. Subclasses must:
|
||||
*
|
||||
* (1) Override CalculateResult for the off-the-main-thread computation.
|
||||
* NSS functionality may only be accessed within CalculateResult.
|
||||
* (2) Override ReleaseNSSResources to release references to all NSS
|
||||
* resources (that do implement nsNSSShutDownObject themselves).
|
||||
* (3) Override CallCallback() for the on-the-main-thread call of the
|
||||
* callback.
|
||||
*
|
||||
* CalculateResult, ReleaseNSSResources, and CallCallback are called in order,
|
||||
* except CalculateResult might be skipped if NSS is shut down before it can
|
||||
* be called; in that case ReleaseNSSResources will be called and then
|
||||
* CallCallback will be called with an error code.
|
||||
*/
|
||||
class CryptoTask : public nsRunnable,
|
||||
public nsNSSShutDownObject
|
||||
{
|
||||
public:
|
||||
template <size_t LEN>
|
||||
nsresult Dispatch(const char (&taskThreadName)[LEN])
|
||||
{
|
||||
MOZ_STATIC_ASSERT(LEN <= 15,
|
||||
"Thread name must be no more than 15 characters");
|
||||
return Dispatch(nsDependentCString(taskThreadName));
|
||||
}
|
||||
|
||||
protected:
|
||||
CryptoTask()
|
||||
: mRv(NS_ERROR_NOT_INITIALIZED),
|
||||
mReleasedNSSResources(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~CryptoTask();
|
||||
|
||||
/**
|
||||
* Called on a background thread (never the main thread). If CalculateResult
|
||||
* is called, then its result will be passed to CallCallback on the main
|
||||
* thread.
|
||||
*/
|
||||
virtual nsresult CalculateResult() = 0;
|
||||
|
||||
/**
|
||||
* Called on the main thread during NSS shutdown or just before CallCallback
|
||||
* has been called. All NSS resources must be released. Usually, this just
|
||||
* means assigning nullptr to the ScopedNSSType-based memory variables.
|
||||
*/
|
||||
virtual void ReleaseNSSResources() = 0;
|
||||
|
||||
/**
|
||||
* Called on the main thread with the result from CalculateResult() or
|
||||
* with an error code if NSS was shut down before CalculateResult could
|
||||
* be called.
|
||||
*/
|
||||
virtual void CallCallback(nsresult rv) = 0;
|
||||
|
||||
private:
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL;
|
||||
virtual void virtualDestroyNSSReference() MOZ_OVERRIDE MOZ_FINAL;
|
||||
|
||||
nsresult Dispatch(const nsACString & taskThreadName);
|
||||
|
||||
nsresult mRv;
|
||||
bool mReleasedNSSResources;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla__CryptoTask_h
|
|
@ -20,6 +20,7 @@ GRE_MODULE = 1
|
|||
LIBXUL_LIBRARY = 1
|
||||
|
||||
CPPSRCS = \
|
||||
CryptoTask.cpp \
|
||||
nsCERTValInParamWrapper.cpp \
|
||||
nsNSSCleaner.cpp \
|
||||
nsCertOverrideService.cpp \
|
||||
|
@ -90,6 +91,7 @@ DEFINES += \
|
|||
$(NULL)
|
||||
|
||||
EXPORTS += \
|
||||
CryptoTask.h \
|
||||
nsNSSShutDown.h \
|
||||
ScopedNSSTypes.h \
|
||||
$(NULL)
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#ifndef mozilla_ScopedNSSTypes_h
|
||||
#define mozilla_ScopedNSSTypes_h
|
||||
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/mozalloc_oom.h"
|
||||
#include "mozilla/Scoped.h"
|
||||
|
||||
#include "prio.h"
|
||||
|
@ -15,9 +17,62 @@
|
|||
#include "keyhi.h"
|
||||
#include "pk11pub.h"
|
||||
#include "sechash.h"
|
||||
#include "secpkcs7.h"
|
||||
#include "prerror.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// It is very common to cast between char* and uint8_t* when doing crypto stuff.
|
||||
// Here, we provide more type-safe wrappers around reinterpret_cast so you don't
|
||||
// shoot yourself in the foot by reinterpret_casting completely unrelated types.
|
||||
|
||||
inline char *
|
||||
char_ptr_cast(uint8_t * p) { return reinterpret_cast<char *>(p); }
|
||||
|
||||
inline const char *
|
||||
char_ptr_cast(const uint8_t * p) { return reinterpret_cast<const char *>(p); }
|
||||
|
||||
inline uint8_t *
|
||||
uint8_t_ptr_cast(char * p) { return reinterpret_cast<uint8_t*>(p); }
|
||||
|
||||
inline const uint8_t *
|
||||
uint8_t_ptr_cast(const char * p) { return reinterpret_cast<const uint8_t*>(p); }
|
||||
|
||||
// NSPR APIs use PRStatus/PR_GetError and NSS APIs use SECStatus/PR_GetError to
|
||||
// report success/failure. These funtions make it more convenient and *safer*
|
||||
// to translate NSPR/NSS results to nsresult. They are safer because they
|
||||
// refuse to traslate any bad PRStatus/SECStatus into an NS_OK, even when the
|
||||
// NSPR/NSS function forgot to call PR_SetError.
|
||||
|
||||
// IMPORTANT: This must be called immediately after the function that set the
|
||||
// error code. Prefer using MapSECStatus to this.
|
||||
inline nsresult
|
||||
PRErrorCode_to_nsresult(PRErrorCode error)
|
||||
{
|
||||
if (!error) {
|
||||
MOZ_NOT_REACHED("Function failed without calling PR_GetError");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// From NSSErrorsService::GetXPCOMFromNSSError
|
||||
// XXX Don't make up nsresults, it's supposed to be an enum (bug 778113)
|
||||
return (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
|
||||
-1 * error);
|
||||
}
|
||||
|
||||
// IMPORTANT: This must be called immediately after the function returning the
|
||||
// SECStatus result. The recommended usage is:
|
||||
// nsresult rv = MapSECStatus(f(x, y, z));
|
||||
inline nsresult
|
||||
MapSECStatus(SECStatus rv)
|
||||
{
|
||||
if (rv == SECSuccess)
|
||||
return NS_OK;
|
||||
|
||||
PRErrorCode error = PR_GetError();
|
||||
return PRErrorCode_to_nsresult(error);
|
||||
}
|
||||
|
||||
// Alphabetical order by NSS type
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc,
|
||||
PRFileDesc,
|
||||
|
@ -47,10 +102,6 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCERTValidity,
|
|||
CERTValidity,
|
||||
CERT_DestroyValidity)
|
||||
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedHASHContext,
|
||||
HASHContext,
|
||||
HASH_Destroy)
|
||||
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedNSSCMSMessage,
|
||||
NSSCMSMessage,
|
||||
NSS_CMSMessage_Destroy)
|
||||
|
@ -58,6 +109,93 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedNSSCMSSignedData,
|
|||
NSSCMSSignedData,
|
||||
NSS_CMSSignedData_Destroy)
|
||||
|
||||
namespace psm {
|
||||
|
||||
inline void
|
||||
PK11_DestroyContext_true(PK11Context * ctx) {
|
||||
PK11_DestroyContext(ctx, true);
|
||||
}
|
||||
|
||||
} // namespace mozilla::psm
|
||||
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11Context,
|
||||
PK11Context,
|
||||
mozilla::psm::PK11_DestroyContext_true)
|
||||
|
||||
/** A more convenient way of dealing with digests calculated into
|
||||
* stack-allocated buffers.
|
||||
*
|
||||
* Typical usage, for digesting a buffer in memory:
|
||||
*
|
||||
* Digest digest;
|
||||
* nsresult rv = digest.DigestBuf(SEC_OID_SHA256, mybuffer, myBufferLen);
|
||||
* NS_ENSURE_SUCCESS(rv, rv);
|
||||
* rv = MapSECStatus(SomeNSSFunction(..., digest.get(), ...));
|
||||
*
|
||||
* Less typical usage, for digesting while doing streaming I/O and similar:
|
||||
*
|
||||
* Digest digest;
|
||||
* ScopedPK11Context digestContext(PK11_CreateDigestContext(SEC_OID_SHA1));
|
||||
* NS_ENSURE_TRUE(digestContext, NS_ERROR_OUT_OF_MEMORY);
|
||||
* rv = MapSECStatus(PK11_DigestBegin(digestContext));
|
||||
* NS_ENSURE_SUCCESS(rv, rv);
|
||||
* for (...) {
|
||||
* rv = MapSECStatus(PK11_DigestOp(digestContext, ...));
|
||||
* NS_ENSURE_SUCCESS(rv, rv);
|
||||
* }
|
||||
* rv = digestContext.End(SEC_OID_SHA1, digestContext);
|
||||
* NS_ENSURE_SUCCESS(rv, rv)
|
||||
*/
|
||||
class Digest
|
||||
{
|
||||
public:
|
||||
Digest()
|
||||
{
|
||||
item.type = siBuffer;
|
||||
item.data = buf;
|
||||
item.len = 0;
|
||||
}
|
||||
|
||||
nsresult DigestBuf(SECOidTag hashAlg, const uint8_t * buf, uint32_t len)
|
||||
{
|
||||
nsresult rv = SetLength(hashAlg);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return MapSECStatus(PK11_HashBuf(hashAlg, item.data, buf, len));
|
||||
}
|
||||
|
||||
nsresult End(SECOidTag hashAlg, ScopedPK11Context & context)
|
||||
{
|
||||
nsresult rv = SetLength(hashAlg);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
uint32_t len;
|
||||
rv = MapSECStatus(PK11_DigestFinal(context, item.data, &len, item.len));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
context = nullptr;
|
||||
NS_ENSURE_TRUE(len == item.len, NS_ERROR_UNEXPECTED);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const SECItem & get() const { return item; }
|
||||
|
||||
private:
|
||||
nsresult SetLength(SECOidTag hashType)
|
||||
{
|
||||
switch (hashType)
|
||||
{
|
||||
case SEC_OID_SHA1: item.len = SHA1_LENGTH; break;
|
||||
case SEC_OID_SHA256: item.len = SHA256_LENGTH; break;
|
||||
case SEC_OID_SHA384: item.len = SHA384_LENGTH; break;
|
||||
case SEC_OID_SHA512: item.len = SHA512_LENGTH; break;
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint8_t buf[HASH_LENGTH_MAX];
|
||||
SECItem item;
|
||||
};
|
||||
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11SlotInfo,
|
||||
PK11SlotInfo,
|
||||
|
@ -69,6 +207,59 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPK11SymKey,
|
|||
PK11SymKey,
|
||||
PK11_FreeSymKey)
|
||||
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSEC_PKCS7ContentInfo,
|
||||
SEC_PKCS7ContentInfo,
|
||||
SEC_PKCS7DestroyContentInfo)
|
||||
|
||||
// Wrapper around NSS's SECItem_AllocItem that handles OOM the same way as
|
||||
// other allocators.
|
||||
inline void
|
||||
SECITEM_AllocItem(SECItem & item, uint32_t len)
|
||||
{
|
||||
if (MOZ_UNLIKELY(!SECITEM_AllocItem(nullptr, &item, len))) {
|
||||
mozalloc_handle_oom(len);
|
||||
if (MOZ_UNLIKELY(!SECITEM_AllocItem(nullptr, &item, len))) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScopedAutoSECItem MOZ_FINAL : public SECItem
|
||||
{
|
||||
public:
|
||||
ScopedAutoSECItem(uint32_t initialAllocatedLen = 0)
|
||||
{
|
||||
data = NULL;
|
||||
len = 0;
|
||||
if (initialAllocatedLen > 0) {
|
||||
SECITEM_AllocItem(*this, initialAllocatedLen);
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
SECITEM_FreeItem(this, false);
|
||||
}
|
||||
|
||||
~ScopedAutoSECItem()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
namespace psm {
|
||||
|
||||
inline void SECITEM_FreeItem_true(SECItem * s)
|
||||
{
|
||||
return SECITEM_FreeItem(s, true);
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECItem,
|
||||
::SECItem,
|
||||
::mozilla::psm::SECITEM_FreeItem_true)
|
||||
|
||||
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECKEYPrivateKey,
|
||||
SECKEYPrivateKey,
|
||||
SECKEY_DestroyPrivateKey)
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "nsNSSShutDown.h"
|
||||
#include "nsSmartCardEvent.h"
|
||||
#include "nsIKeyModule.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
|
||||
#include "nss.h"
|
||||
#include "pk11func.h"
|
||||
|
@ -2028,7 +2029,7 @@ nsNSSComponent::VerifySignature(const char* aRSABuf, uint32_t aRSABufLen,
|
|||
*aPrincipal = nullptr;
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
SEC_PKCS7ContentInfo * p7_info = nullptr;
|
||||
ScopedSEC_PKCS7ContentInfo p7_info;
|
||||
unsigned char hash[SHA1_LENGTH];
|
||||
|
||||
SECItem item;
|
||||
|
@ -2127,8 +2128,6 @@ nsNSSComponent::VerifySignature(const char* aRSABuf, uint32_t aRSABufLen,
|
|||
} while (0);
|
||||
}
|
||||
|
||||
SEC_PKCS7DestroyContentInfo(p7_info);
|
||||
|
||||
return rv2;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
|
||||
#include "nss.h"
|
||||
#include "pk11pub.h"
|
||||
|
@ -60,34 +61,6 @@ Base64UrlEncodeImpl(const nsACString & utf8Input, nsACString & result)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
PRErrorCode_to_nsresult(PRErrorCode error)
|
||||
{
|
||||
if (!error) {
|
||||
MOZ_NOT_REACHED("Function failed without calling PR_GetError");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// From NSSErrorsService::GetXPCOMFromNSSError
|
||||
// XXX Don't make up nsresults, it's supposed to be an enum (bug 778113)
|
||||
return (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
|
||||
-1 * error);
|
||||
}
|
||||
|
||||
// IMPORTANT: This must be called immediately after the function returning the
|
||||
// SECStatus result. The recommended usage is:
|
||||
// nsresult rv = MapSECStatus(f(x, y, z));
|
||||
nsresult
|
||||
MapSECStatus(SECStatus rv)
|
||||
{
|
||||
if (rv == SECSuccess)
|
||||
return NS_OK;
|
||||
|
||||
PRErrorCode error = PR_GetError();
|
||||
return PRErrorCode_to_nsresult(error);
|
||||
}
|
||||
|
||||
#define DSA_KEY_TYPE_STRING (NS_LITERAL_CSTRING("DS160"))
|
||||
#define RSA_KEY_TYPE_STRING (NS_LITERAL_CSTRING("RS256"))
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче