michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: #include "prlog.h" michael@0: #include "nsEntropyCollector.h" michael@0: #include "nsAlgorithm.h" michael@0: #include michael@0: michael@0: nsEntropyCollector::nsEntropyCollector() michael@0: :mBytesCollected(0), mWritePointer(mEntropyCache) michael@0: { michael@0: // We could use the uninitialized memory in mEntropyCache as initial michael@0: // random data, but that means (if any entropy is collected before NSS michael@0: // initialization and then forwarded) that we'll get warnings from michael@0: // tools like valgrind for every later operation that depends on the michael@0: // entropy. michael@0: memset(mEntropyCache, 0, sizeof(mEntropyCache)); michael@0: } michael@0: michael@0: nsEntropyCollector::~nsEntropyCollector() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsEntropyCollector, michael@0: nsIEntropyCollector, michael@0: nsIBufEntropyCollector) michael@0: michael@0: NS_IMETHODIMP michael@0: nsEntropyCollector::RandomUpdate(void *new_entropy, int32_t bufLen) michael@0: { michael@0: if (bufLen > 0) { michael@0: if (mForwardTarget) { michael@0: return mForwardTarget->RandomUpdate(new_entropy, bufLen); michael@0: } michael@0: else { michael@0: const unsigned char *InputPointer = (const unsigned char *)new_entropy; michael@0: const unsigned char *PastEndPointer = mEntropyCache + entropy_buffer_size; michael@0: michael@0: // if the input is large, we only take as much as we can store michael@0: int32_t bytes_wanted = std::min(bufLen, int32_t(entropy_buffer_size)); michael@0: michael@0: // remember the number of bytes we will have after storing new_entropy michael@0: mBytesCollected = std::min(int32_t(entropy_buffer_size), michael@0: mBytesCollected + bytes_wanted); michael@0: michael@0: // as the above statements limit bytes_wanted to the entropy_buffer_size, michael@0: // this loop will iterate at most twice. michael@0: while (bytes_wanted > 0) { michael@0: michael@0: // how many bytes to end of cyclic buffer? michael@0: const int32_t space_to_end = PastEndPointer - mWritePointer; michael@0: michael@0: // how many bytes can we copy, not reaching the end of the buffer? michael@0: const int32_t this_time = std::min(space_to_end, bytes_wanted); michael@0: michael@0: // copy at most to the end of the cyclic buffer michael@0: for (int32_t i = 0; i < this_time; ++i) { michael@0: michael@0: unsigned int old = *mWritePointer; michael@0: michael@0: // combine new and old value already stored in buffer michael@0: // this logic comes from PSM 1 michael@0: *mWritePointer++ = ((old << 1) | (old >> 7)) ^ *InputPointer++; michael@0: } michael@0: michael@0: PR_ASSERT(mWritePointer <= PastEndPointer); michael@0: PR_ASSERT(mWritePointer >= mEntropyCache); michael@0: michael@0: // have we arrived at the end of the buffer? michael@0: if (PastEndPointer == mWritePointer) { michael@0: // reset write pointer back to begining of our buffer michael@0: mWritePointer = mEntropyCache; michael@0: } michael@0: michael@0: // subtract the number of bytes we have already copied michael@0: bytes_wanted -= this_time; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsEntropyCollector::ForwardTo(nsIEntropyCollector *aCollector) michael@0: { michael@0: NS_PRECONDITION(!mForwardTarget, "|ForwardTo| should only be called once."); michael@0: michael@0: mForwardTarget = aCollector; michael@0: mForwardTarget->RandomUpdate(mEntropyCache, mBytesCollected); michael@0: mBytesCollected = 0; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsEntropyCollector::DontForward() michael@0: { michael@0: mForwardTarget = nullptr; michael@0: return NS_OK; michael@0: }