diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /security/manager/ssl/nsCryptoHash.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/manager/ssl/nsCryptoHash.cpp')
-rw-r--r-- | security/manager/ssl/nsCryptoHash.cpp | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/security/manager/ssl/nsCryptoHash.cpp b/security/manager/ssl/nsCryptoHash.cpp new file mode 100644 index 0000000000..9304a0cd99 --- /dev/null +++ b/security/manager/ssl/nsCryptoHash.cpp @@ -0,0 +1,334 @@ +/* -*- 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 "nsCryptoHash.h" + +#include <algorithm> + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Base64.h" +#include "mozilla/Casting.h" +#include "nsDependentString.h" +#include "nsIInputStream.h" +#include "nsIKeyModule.h" +#include "nsString.h" +#include "pk11pub.h" +#include "sechash.h" + +using namespace mozilla; + +namespace { + +static const uint64_t STREAM_BUFFER_SIZE = 4096; + +} // namespace + +//--------------------------------------------- +// Implementing nsICryptoHash +//--------------------------------------------- + +nsCryptoHash::nsCryptoHash() : mHashContext(nullptr), mInitialized(false) {} + +NS_IMPL_ISUPPORTS(nsCryptoHash, nsICryptoHash) + +NS_IMETHODIMP +nsCryptoHash::Init(uint32_t algorithm) { + HASH_HashType hashType; + switch (algorithm) { + case nsICryptoHash::MD5: + hashType = HASH_AlgMD5; + break; + case nsICryptoHash::SHA1: + hashType = HASH_AlgSHA1; + break; + case nsICryptoHash::SHA256: + hashType = HASH_AlgSHA256; + break; + case nsICryptoHash::SHA384: + hashType = HASH_AlgSHA384; + break; + case nsICryptoHash::SHA512: + hashType = HASH_AlgSHA512; + break; + default: + return NS_ERROR_INVALID_ARG; + } + + if (mHashContext) { + if (!mInitialized && HASH_GetType(mHashContext.get()) == hashType) { + mInitialized = true; + HASH_Begin(mHashContext.get()); + return NS_OK; + } + + // Destroy current hash context if the type was different + // or Finish method wasn't called. + mHashContext = nullptr; + mInitialized = false; + } + + mHashContext.reset(HASH_Create(hashType)); + if (!mHashContext) { + return NS_ERROR_INVALID_ARG; + } + + HASH_Begin(mHashContext.get()); + mInitialized = true; + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHash::InitWithString(const nsACString& aAlgorithm) { + 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) { + if (!mInitialized) { + return NS_ERROR_NOT_INITIALIZED; + } + + HASH_Update(mHashContext.get(), data, len); + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHash::UpdateFromStream(nsIInputStream* data, uint32_t aLen) { + 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[STREAM_BUFFER_SIZE]; + while (len > 0) { + uint64_t readLimit = std::min<uint64_t>(STREAM_BUFFER_SIZE, len); + uint32_t read; + rv = data->Read(buffer, AssertedCast<uint32_t>(readLimit), &read); + if (NS_FAILED(rv)) { + return rv; + } + + rv = Update(BitwiseCast<uint8_t*>(buffer), read); + if (NS_FAILED(rv)) { + return rv; + } + + len -= read; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHash::Finish(bool ascii, nsACString& _retval) { + if (!mInitialized) { + return NS_ERROR_NOT_INITIALIZED; + } + + uint32_t hashLen = 0; + unsigned char buffer[HASH_LENGTH_MAX]; + HASH_End(mHashContext.get(), buffer, &hashLen, HASH_LENGTH_MAX); + + mInitialized = false; + + if (ascii) { + nsDependentCSubstring dataStr(BitwiseCast<char*>(buffer), hashLen); + return Base64Encode(dataStr, _retval); + } + + _retval.Assign(BitwiseCast<char*>(buffer), hashLen); + return NS_OK; +} + +//--------------------------------------------- +// Implementing nsICryptoHMAC +//--------------------------------------------- + +NS_IMPL_ISUPPORTS(nsCryptoHMAC, nsICryptoHMAC) + +nsCryptoHMAC::nsCryptoHMAC() : mHMACContext(nullptr) {} + +NS_IMETHODIMP +nsCryptoHMAC::Init(uint32_t aAlgorithm, nsIKeyObject* aKeyObject) { + if (mHMACContext) { + mHMACContext = nullptr; + } + + CK_MECHANISM_TYPE mechType; + switch (aAlgorithm) { + case nsICryptoHMAC::MD5: + mechType = CKM_MD5_HMAC; + break; + case nsICryptoHMAC::SHA1: + mechType = CKM_SHA_1_HMAC; + break; + case nsICryptoHMAC::SHA256: + mechType = CKM_SHA256_HMAC; + break; + case nsICryptoHMAC::SHA384: + mechType = CKM_SHA384_HMAC; + break; + case nsICryptoHMAC::SHA512: + mechType = 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.reset( + PK11_CreateContextBySymKey(mechType, CKA_SIGN, key, &rawData)); + NS_ENSURE_TRUE(mHMACContext, NS_ERROR_FAILURE); + + if (PK11_DigestBegin(mHMACContext.get()) != SECSuccess) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHMAC::Update(const uint8_t* aData, uint32_t aLen) { + if (!mHMACContext) return NS_ERROR_NOT_INITIALIZED; + + if (!aData) return NS_ERROR_INVALID_ARG; + + if (PK11_DigestOp(mHMACContext.get(), aData, aLen) != SECSuccess) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHMAC::UpdateFromStream(nsIInputStream* aStream, uint32_t aLen) { + 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[STREAM_BUFFER_SIZE]; + while (len > 0) { + uint64_t readLimit = std::min<uint64_t>(STREAM_BUFFER_SIZE, len); + uint32_t read; + rv = aStream->Read(buffer, AssertedCast<uint32_t>(readLimit), &read); + if (NS_FAILED(rv)) { + return rv; + } + + if (read == 0) { + return NS_BASE_STREAM_CLOSED; + } + + rv = Update(BitwiseCast<uint8_t*>(buffer), read); + if (NS_FAILED(rv)) { + return rv; + } + + len -= read; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHMAC::Finish(bool aASCII, nsACString& _retval) { + if (!mHMACContext) return NS_ERROR_NOT_INITIALIZED; + + uint32_t hashLen = 0; + unsigned char buffer[HASH_LENGTH_MAX]; + SECStatus srv = + PK11_DigestFinal(mHMACContext.get(), buffer, &hashLen, HASH_LENGTH_MAX); + if (srv != SECSuccess) { + return NS_ERROR_FAILURE; + } + + if (aASCII) { + nsDependentCSubstring dataStr(BitwiseCast<char*>(buffer), hashLen); + return Base64Encode(dataStr, _retval); + } + + _retval.Assign(BitwiseCast<char*>(buffer), hashLen); + return NS_OK; +} + +NS_IMETHODIMP +nsCryptoHMAC::Reset() { + if (PK11_DigestBegin(mHMACContext.get()) != SECSuccess) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} |