diff options
Diffstat (limited to 'security/manager/ssl/nsCryptoHash.cpp')
-rw-r--r-- | security/manager/ssl/nsCryptoHash.cpp | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/security/manager/ssl/nsCryptoHash.cpp b/security/manager/ssl/nsCryptoHash.cpp new file mode 100644 index 000000000..d57eb5f58 --- /dev/null +++ b/security/manager/ssl/nsCryptoHash.cpp @@ -0,0 +1,438 @@ +/* -*- 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 <algorithm> + +#include "nsCryptoHash.h" + +#include "nsIInputStream.h" +#include "nsIKeyModule.h" + +#include "nsString.h" + +#include "sechash.h" +#include "pk11pub.h" +#include "base64.h" + +#define NS_CRYPTO_HASH_BUFFER_SIZE 4096 + +//--------------------------------------------- +// Implementing nsICryptoHash +//--------------------------------------------- + +nsCryptoHash::nsCryptoHash() + : mHashContext(nullptr) + , mInitialized(false) +{ +} + +nsCryptoHash::~nsCryptoHash() +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return; + } + destructorSafeDestroyNSSReference(); + shutdown(ShutdownCalledFrom::Object); +} + +void +nsCryptoHash::virtualDestroyNSSReference() +{ + destructorSafeDestroyNSSReference(); +} + +void +nsCryptoHash::destructorSafeDestroyNSSReference() +{ + if (mHashContext) + HASH_Destroy(mHashContext); + mHashContext = nullptr; +} + +NS_IMPL_ISUPPORTS(nsCryptoHash, nsICryptoHash) + +NS_IMETHODIMP +nsCryptoHash::Init(uint32_t algorithm) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + HASH_HashType hashType = (HASH_HashType)algorithm; + if (mHashContext) + { + if ((!mInitialized) && (HASH_GetType(mHashContext) == hashType)) + { + mInitialized = true; + HASH_Begin(mHashContext); + return NS_OK; + } + + // Destroy current hash context if the type was different + // or Finish method wasn't called. + HASH_Destroy(mHashContext); + mInitialized = false; + } + + mHashContext = HASH_Create(hashType); + if (!mHashContext) + return NS_ERROR_INVALID_ARG; + + HASH_Begin(mHashContext); + mInitialized = true; + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHash::InitWithString(const nsACString & aAlgorithm) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (aAlgorithm.LowerCaseEqualsLiteral("md2")) + return Init(nsICryptoHash::MD2); + + if (aAlgorithm.LowerCaseEqualsLiteral("md5")) + return Init(nsICryptoHash::MD5); + + if (aAlgorithm.LowerCaseEqualsLiteral("sha1")) + return Init(nsICryptoHash::SHA1); + + if (aAlgorithm.LowerCaseEqualsLiteral("sha256")) + return Init(nsICryptoHash::SHA256); + + if (aAlgorithm.LowerCaseEqualsLiteral("sha384")) + return Init(nsICryptoHash::SHA384); + + if (aAlgorithm.LowerCaseEqualsLiteral("sha512")) + return Init(nsICryptoHash::SHA512); + + return NS_ERROR_INVALID_ARG; +} + +NS_IMETHODIMP +nsCryptoHash::Update(const uint8_t *data, uint32_t len) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (!mInitialized) + return NS_ERROR_NOT_INITIALIZED; + + HASH_Update(mHashContext, data, len); + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHash::UpdateFromStream(nsIInputStream *data, uint32_t aLen) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (!mInitialized) + return NS_ERROR_NOT_INITIALIZED; + + if (!data) + return NS_ERROR_INVALID_ARG; + + uint64_t n; + nsresult rv = data->Available(&n); + if (NS_FAILED(rv)) + return rv; + + // if the user has passed UINT32_MAX, then read + // everything in the stream + + uint64_t len = aLen; + if (aLen == UINT32_MAX) + len = n; + + // So, if the stream has NO data available for the hash, + // or if the data available is less then what the caller + // requested, we can not fulfill the hash update. In this + // case, just return NS_ERROR_NOT_AVAILABLE indicating + // that there is not enough data in the stream to satisify + // the request. + + if (n == 0 || n < len) + return NS_ERROR_NOT_AVAILABLE; + + char buffer[NS_CRYPTO_HASH_BUFFER_SIZE]; + uint32_t read, readLimit; + + while(NS_SUCCEEDED(rv) && len>0) + { + readLimit = (uint32_t)std::min<uint64_t>(NS_CRYPTO_HASH_BUFFER_SIZE, len); + + rv = data->Read(buffer, readLimit, &read); + + if (NS_SUCCEEDED(rv)) + rv = Update((const uint8_t*)buffer, read); + + len -= read; + } + + return rv; +} + +NS_IMETHODIMP +nsCryptoHash::Finish(bool ascii, nsACString & _retval) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (!mInitialized) + return NS_ERROR_NOT_INITIALIZED; + + uint32_t hashLen = 0; + unsigned char buffer[HASH_LENGTH_MAX]; + unsigned char* pbuffer = buffer; + + HASH_End(mHashContext, pbuffer, &hashLen, HASH_LENGTH_MAX); + + mInitialized = false; + + if (ascii) + { + UniquePORTString asciiData(BTOA_DataToAscii(buffer, hashLen)); + NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY); + + _retval.Assign(asciiData.get()); + } + else + { + _retval.Assign((const char*)buffer, hashLen); + } + + return NS_OK; +} + +//--------------------------------------------- +// Implementing nsICryptoHMAC +//--------------------------------------------- + +NS_IMPL_ISUPPORTS(nsCryptoHMAC, nsICryptoHMAC) + +nsCryptoHMAC::nsCryptoHMAC() +{ + mHMACContext = nullptr; +} + +nsCryptoHMAC::~nsCryptoHMAC() +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return; + } + destructorSafeDestroyNSSReference(); + shutdown(ShutdownCalledFrom::Object); +} + +void +nsCryptoHMAC::virtualDestroyNSSReference() +{ + destructorSafeDestroyNSSReference(); +} + +void +nsCryptoHMAC::destructorSafeDestroyNSSReference() +{ + if (mHMACContext) + PK11_DestroyContext(mHMACContext, true); + mHMACContext = nullptr; +} + +NS_IMETHODIMP +nsCryptoHMAC::Init(uint32_t aAlgorithm, nsIKeyObject *aKeyObject) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (mHMACContext) + { + PK11_DestroyContext(mHMACContext, true); + mHMACContext = nullptr; + } + + CK_MECHANISM_TYPE HMACMechType; + switch (aAlgorithm) + { + case nsCryptoHMAC::MD2: + HMACMechType = CKM_MD2_HMAC; break; + case nsCryptoHMAC::MD5: + HMACMechType = CKM_MD5_HMAC; break; + case nsCryptoHMAC::SHA1: + HMACMechType = CKM_SHA_1_HMAC; break; + case nsCryptoHMAC::SHA256: + HMACMechType = CKM_SHA256_HMAC; break; + case nsCryptoHMAC::SHA384: + HMACMechType = CKM_SHA384_HMAC; break; + case nsCryptoHMAC::SHA512: + HMACMechType = CKM_SHA512_HMAC; break; + default: + return NS_ERROR_INVALID_ARG; + } + + NS_ENSURE_ARG_POINTER(aKeyObject); + + nsresult rv; + + int16_t keyType; + rv = aKeyObject->GetType(&keyType); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ENSURE_TRUE(keyType == nsIKeyObject::SYM_KEY, NS_ERROR_INVALID_ARG); + + PK11SymKey* key; + // GetKeyObj doesn't addref the key + rv = aKeyObject->GetKeyObj(&key); + NS_ENSURE_SUCCESS(rv, rv); + + SECItem rawData; + rawData.data = 0; + rawData.len = 0; + mHMACContext = PK11_CreateContextBySymKey( + HMACMechType, CKA_SIGN, key, &rawData); + NS_ENSURE_TRUE(mHMACContext, NS_ERROR_FAILURE); + + SECStatus ss = PK11_DigestBegin(mHMACContext); + NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); + + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHMAC::Update(const uint8_t *aData, uint32_t aLen) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (!mHMACContext) + return NS_ERROR_NOT_INITIALIZED; + + if (!aData) + return NS_ERROR_INVALID_ARG; + + SECStatus ss = PK11_DigestOp(mHMACContext, aData, aLen); + NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); + + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHMAC::UpdateFromStream(nsIInputStream *aStream, uint32_t aLen) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (!mHMACContext) + return NS_ERROR_NOT_INITIALIZED; + + if (!aStream) + return NS_ERROR_INVALID_ARG; + + uint64_t n; + nsresult rv = aStream->Available(&n); + if (NS_FAILED(rv)) + return rv; + + // if the user has passed UINT32_MAX, then read + // everything in the stream + + uint64_t len = aLen; + if (aLen == UINT32_MAX) + len = n; + + // So, if the stream has NO data available for the hash, + // or if the data available is less then what the caller + // requested, we can not fulfill the HMAC update. In this + // case, just return NS_ERROR_NOT_AVAILABLE indicating + // that there is not enough data in the stream to satisify + // the request. + + if (n == 0 || n < len) + return NS_ERROR_NOT_AVAILABLE; + + char buffer[NS_CRYPTO_HASH_BUFFER_SIZE]; + uint32_t read, readLimit; + + while(NS_SUCCEEDED(rv) && len > 0) + { + readLimit = (uint32_t)std::min<uint64_t>(NS_CRYPTO_HASH_BUFFER_SIZE, len); + + rv = aStream->Read(buffer, readLimit, &read); + if (read == 0) + return NS_BASE_STREAM_CLOSED; + + if (NS_SUCCEEDED(rv)) + rv = Update((const uint8_t*)buffer, read); + + len -= read; + } + + return rv; +} + +NS_IMETHODIMP +nsCryptoHMAC::Finish(bool aASCII, nsACString & _retval) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (!mHMACContext) + return NS_ERROR_NOT_INITIALIZED; + + uint32_t hashLen = 0; + unsigned char buffer[HASH_LENGTH_MAX]; + unsigned char* pbuffer = buffer; + + PK11_DigestFinal(mHMACContext, pbuffer, &hashLen, HASH_LENGTH_MAX); + if (aASCII) + { + UniquePORTString asciiData(BTOA_DataToAscii(buffer, hashLen)); + NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY); + + _retval.Assign(asciiData.get()); + } + else + { + _retval.Assign((const char*)buffer, hashLen); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHMAC::Reset() +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_NOT_AVAILABLE; + } + + SECStatus ss = PK11_DigestBegin(mHMACContext); + NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); + + return NS_OK; +} |