summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/nsCryptoHash.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/nsCryptoHash.cpp')
-rw-r--r--security/manager/ssl/nsCryptoHash.cpp438
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;
+}