michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG 1 michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: #include "nsCryptoHash.h" michael@0: michael@0: #include "nsIInputStream.h" michael@0: #include "nsIKeyModule.h" michael@0: michael@0: #include "nsString.h" michael@0: michael@0: #include "sechash.h" michael@0: #include "pk11pub.h" michael@0: #include "base64.h" michael@0: michael@0: #define NS_CRYPTO_HASH_BUFFER_SIZE 4096 michael@0: michael@0: //--------------------------------------------- michael@0: // Implementing nsICryptoHash michael@0: //--------------------------------------------- michael@0: michael@0: nsCryptoHash::nsCryptoHash() michael@0: : mHashContext(nullptr) michael@0: , mInitialized(false) michael@0: { michael@0: } michael@0: michael@0: nsCryptoHash::~nsCryptoHash() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) { michael@0: return; michael@0: } michael@0: destructorSafeDestroyNSSReference(); michael@0: shutdown(calledFromObject); michael@0: } michael@0: michael@0: void nsCryptoHash::virtualDestroyNSSReference() michael@0: { michael@0: destructorSafeDestroyNSSReference(); michael@0: } michael@0: michael@0: void nsCryptoHash::destructorSafeDestroyNSSReference() michael@0: { michael@0: if (mHashContext) michael@0: HASH_Destroy(mHashContext); michael@0: mHashContext = nullptr; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCryptoHash, nsICryptoHash) michael@0: michael@0: NS_IMETHODIMP michael@0: nsCryptoHash::Init(uint32_t algorithm) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: michael@0: HASH_HashType hashType = (HASH_HashType)algorithm; michael@0: if (mHashContext) michael@0: { michael@0: if ((!mInitialized) && (HASH_GetType(mHashContext) == hashType)) michael@0: { michael@0: mInitialized = true; michael@0: HASH_Begin(mHashContext); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Destroy current hash context if the type was different michael@0: // or Finish method wasn't called. michael@0: HASH_Destroy(mHashContext); michael@0: mInitialized = false; michael@0: } michael@0: michael@0: mHashContext = HASH_Create(hashType); michael@0: if (!mHashContext) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: HASH_Begin(mHashContext); michael@0: mInitialized = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCryptoHash::InitWithString(const nsACString & aAlgorithm) michael@0: { michael@0: if (aAlgorithm.LowerCaseEqualsLiteral("md2")) michael@0: return Init(nsICryptoHash::MD2); michael@0: michael@0: if (aAlgorithm.LowerCaseEqualsLiteral("md5")) michael@0: return Init(nsICryptoHash::MD5); michael@0: michael@0: if (aAlgorithm.LowerCaseEqualsLiteral("sha1")) michael@0: return Init(nsICryptoHash::SHA1); michael@0: michael@0: if (aAlgorithm.LowerCaseEqualsLiteral("sha256")) michael@0: return Init(nsICryptoHash::SHA256); michael@0: michael@0: if (aAlgorithm.LowerCaseEqualsLiteral("sha384")) michael@0: return Init(nsICryptoHash::SHA384); michael@0: michael@0: if (aAlgorithm.LowerCaseEqualsLiteral("sha512")) michael@0: return Init(nsICryptoHash::SHA512); michael@0: michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCryptoHash::Update(const uint8_t *data, uint32_t len) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: michael@0: if (!mInitialized) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: HASH_Update(mHashContext, data, len); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCryptoHash::UpdateFromStream(nsIInputStream *data, uint32_t aLen) michael@0: { michael@0: if (!mInitialized) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: if (!data) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: uint64_t n; michael@0: nsresult rv = data->Available(&n); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // if the user has passed UINT32_MAX, then read michael@0: // everything in the stream michael@0: michael@0: uint64_t len = aLen; michael@0: if (aLen == UINT32_MAX) michael@0: len = n; michael@0: michael@0: // So, if the stream has NO data available for the hash, michael@0: // or if the data available is less then what the caller michael@0: // requested, we can not fulfill the hash update. In this michael@0: // case, just return NS_ERROR_NOT_AVAILABLE indicating michael@0: // that there is not enough data in the stream to satisify michael@0: // the request. michael@0: michael@0: if (n == 0 || n < len) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: char buffer[NS_CRYPTO_HASH_BUFFER_SIZE]; michael@0: uint32_t read, readLimit; michael@0: michael@0: while(NS_SUCCEEDED(rv) && len>0) michael@0: { michael@0: readLimit = (uint32_t)std::min(NS_CRYPTO_HASH_BUFFER_SIZE, len); michael@0: michael@0: rv = data->Read(buffer, readLimit, &read); michael@0: michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = Update((const uint8_t*)buffer, read); michael@0: michael@0: len -= read; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCryptoHash::Finish(bool ascii, nsACString & _retval) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: michael@0: if (!mInitialized) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: uint32_t hashLen = 0; michael@0: unsigned char buffer[HASH_LENGTH_MAX]; michael@0: unsigned char* pbuffer = buffer; michael@0: michael@0: HASH_End(mHashContext, pbuffer, &hashLen, HASH_LENGTH_MAX); michael@0: michael@0: mInitialized = false; michael@0: michael@0: if (ascii) michael@0: { michael@0: char *asciiData = BTOA_DataToAscii(buffer, hashLen); michael@0: NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: _retval.Assign(asciiData); michael@0: PORT_Free(asciiData); michael@0: } michael@0: else michael@0: { michael@0: _retval.Assign((const char*)buffer, hashLen); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //--------------------------------------------- michael@0: // Implementing nsICryptoHMAC michael@0: //--------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCryptoHMAC, nsICryptoHMAC) michael@0: michael@0: nsCryptoHMAC::nsCryptoHMAC() michael@0: { michael@0: mHMACContext = nullptr; michael@0: } michael@0: michael@0: nsCryptoHMAC::~nsCryptoHMAC() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: if (isAlreadyShutDown()) { michael@0: return; michael@0: } michael@0: destructorSafeDestroyNSSReference(); michael@0: shutdown(calledFromObject); michael@0: } michael@0: michael@0: void nsCryptoHMAC::virtualDestroyNSSReference() michael@0: { michael@0: destructorSafeDestroyNSSReference(); michael@0: } michael@0: michael@0: void nsCryptoHMAC::destructorSafeDestroyNSSReference() michael@0: { michael@0: if (mHMACContext) michael@0: PK11_DestroyContext(mHMACContext, true); michael@0: mHMACContext = nullptr; michael@0: } michael@0: michael@0: /* void init (in unsigned long aAlgorithm, in nsIKeyObject aKeyObject); */ michael@0: NS_IMETHODIMP nsCryptoHMAC::Init(uint32_t aAlgorithm, nsIKeyObject *aKeyObject) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: michael@0: if (mHMACContext) michael@0: { michael@0: PK11_DestroyContext(mHMACContext, true); michael@0: mHMACContext = nullptr; michael@0: } michael@0: michael@0: CK_MECHANISM_TYPE HMACMechType; michael@0: switch (aAlgorithm) michael@0: { michael@0: case nsCryptoHMAC::MD2: michael@0: HMACMechType = CKM_MD2_HMAC; break; michael@0: case nsCryptoHMAC::MD5: michael@0: HMACMechType = CKM_MD5_HMAC; break; michael@0: case nsCryptoHMAC::SHA1: michael@0: HMACMechType = CKM_SHA_1_HMAC; break; michael@0: case nsCryptoHMAC::SHA256: michael@0: HMACMechType = CKM_SHA256_HMAC; break; michael@0: case nsCryptoHMAC::SHA384: michael@0: HMACMechType = CKM_SHA384_HMAC; break; michael@0: case nsCryptoHMAC::SHA512: michael@0: HMACMechType = CKM_SHA512_HMAC; break; michael@0: default: michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: NS_ENSURE_ARG_POINTER(aKeyObject); michael@0: michael@0: nsresult rv; michael@0: michael@0: int16_t keyType; michael@0: rv = aKeyObject->GetType(&keyType); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ENSURE_TRUE(keyType == nsIKeyObject::SYM_KEY, NS_ERROR_INVALID_ARG); michael@0: michael@0: PK11SymKey* key; michael@0: // GetKeyObj doesn't addref the key michael@0: rv = aKeyObject->GetKeyObj((void**)&key); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: SECItem rawData; michael@0: rawData.data = 0; michael@0: rawData.len = 0; michael@0: mHMACContext = PK11_CreateContextBySymKey( michael@0: HMACMechType, CKA_SIGN, key, &rawData); michael@0: NS_ENSURE_TRUE(mHMACContext, NS_ERROR_FAILURE); michael@0: michael@0: SECStatus ss = PK11_DigestBegin(mHMACContext); michael@0: NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void update ([array, size_is (aLen), const] in octet aData, in unsigned long aLen); */ michael@0: NS_IMETHODIMP nsCryptoHMAC::Update(const uint8_t *aData, uint32_t aLen) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: michael@0: if (!mHMACContext) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: if (!aData) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: SECStatus ss = PK11_DigestOp(mHMACContext, aData, aLen); michael@0: NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void updateFromStream (in nsIInputStream aStream, in unsigned long aLen); */ michael@0: NS_IMETHODIMP nsCryptoHMAC::UpdateFromStream(nsIInputStream *aStream, uint32_t aLen) michael@0: { michael@0: if (!mHMACContext) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: if (!aStream) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: uint64_t n; michael@0: nsresult rv = aStream->Available(&n); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // if the user has passed UINT32_MAX, then read michael@0: // everything in the stream michael@0: michael@0: uint64_t len = aLen; michael@0: if (aLen == UINT32_MAX) michael@0: len = n; michael@0: michael@0: // So, if the stream has NO data available for the hash, michael@0: // or if the data available is less then what the caller michael@0: // requested, we can not fulfill the HMAC update. In this michael@0: // case, just return NS_ERROR_NOT_AVAILABLE indicating michael@0: // that there is not enough data in the stream to satisify michael@0: // the request. michael@0: michael@0: if (n == 0 || n < len) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: char buffer[NS_CRYPTO_HASH_BUFFER_SIZE]; michael@0: uint32_t read, readLimit; michael@0: michael@0: while(NS_SUCCEEDED(rv) && len > 0) michael@0: { michael@0: readLimit = (uint32_t)std::min(NS_CRYPTO_HASH_BUFFER_SIZE, len); michael@0: michael@0: rv = aStream->Read(buffer, readLimit, &read); michael@0: if (read == 0) michael@0: return NS_BASE_STREAM_CLOSED; michael@0: michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = Update((const uint8_t*)buffer, read); michael@0: michael@0: len -= read; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* ACString finish (in bool aASCII); */ michael@0: NS_IMETHODIMP nsCryptoHMAC::Finish(bool aASCII, nsACString & _retval) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: michael@0: if (!mHMACContext) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: uint32_t hashLen = 0; michael@0: unsigned char buffer[HASH_LENGTH_MAX]; michael@0: unsigned char* pbuffer = buffer; michael@0: michael@0: PK11_DigestFinal(mHMACContext, pbuffer, &hashLen, HASH_LENGTH_MAX); michael@0: if (aASCII) michael@0: { michael@0: char *asciiData = BTOA_DataToAscii(buffer, hashLen); michael@0: NS_ENSURE_TRUE(asciiData, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: _retval.Assign(asciiData); michael@0: PORT_Free(asciiData); michael@0: } michael@0: else michael@0: { michael@0: _retval.Assign((const char*)buffer, hashLen); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void reset (); */ michael@0: NS_IMETHODIMP nsCryptoHMAC::Reset() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: michael@0: SECStatus ss = PK11_DigestBegin(mHMACContext); michael@0: NS_ENSURE_TRUE(ss == SECSuccess, NS_ERROR_FAILURE); michael@0: michael@0: return NS_OK; michael@0: }