Skip to content

Commit

Permalink
Fix
Browse files Browse the repository at this point in the history
  • Loading branch information
TobiasFella committed Oct 12, 2024
1 parent e848043 commit 52d988a
Showing 1 changed file with 0 additions and 171 deletions.
171 changes: 0 additions & 171 deletions Quotient/e2ee/e2ee_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,176 +8,5 @@

#include <QtCore/QRandomGenerator>

#include <openssl/crypto.h>

using namespace Quotient;

QByteArray Quotient::byteArrayForOlm(size_t bufferSize)
{
if (std::in_range<QByteArray::size_type>(bufferSize)) [[likely]]
return { static_cast<QByteArray::size_type>(bufferSize), '\0' };

qCritical(E2EE) << "Buffer size out of QByteArray range:" << bufferSize;
// Zero-length QByteArray is an almost guaranteed way to cause
// an internal error in QOlm* classes, unless checked
return {};
}

void Quotient::_impl::checkForSpanShortfall(QByteArray::size_type inputSize, int neededSize)
{
if (inputSize < neededSize) {
qCCritical(E2EE) << "Not enough bytes to create a valid span: "
<< inputSize << '<' << neededSize
<< "- undefined behaviour imminent";
Q_ASSERT(false);
// Can't help it in Release builds; a span of the given size has
// to be returned regardless, so UB
}
}

void Quotient::fillFromSecureRng(std::span<byte_t> bytes)
{
// Discussion of QRandomGenerator::system() vs. OpenSSL's RAND_bytes
//
// It is a rather close call between the two; to be honest, TL;DR from the
// below is "it doesn't really matter". Going through the source code of
// both libraries, along with reading others' investigations like
// https://github.com/golang/go/issues/33542, left me (@kitsune) with
// the following:
// 1. Both ultimately use more or less the same stuff internally - which is
// is not surprising and is reassuring in TL;DR. Contrary to occasional
// claims on interwebs though, QRandomGenerator does not rely on OpenSSL,
// calling system primitives directly instead.
// 2. Qt's code is massively simpler - again, effectively it's a rather
// thin wrapper around what I know for a reasonably good practice across
// the platforms supported by Quotient.
// 3. OpenSSL is more flexible in that you can specify which RNG engine
// you actually want: you can, e.g., explicitly use the hardware-based
// (aka "true") RNG on modern Intel CPUs. QRandomGenerator "simply"
// offers the nice Qt interface to whatever best the operating system
// provides (and at least Linux kernel happens to also use true RNGs
// where it can, so ¯\(ツ)/¯)
// 3. QRandomGenerator produces stuff in uint32_t (or uint64_t in case of
// QRandomGenerator64) chunks; OpenSSL generates bytes, which is sorta
// more convenient. Quotient never needs quantities of randomness that
// are not multiples of 16 _bytes_; the Q_UNLIKELY branch below is merely
// to cover an edge case that should never happen in the first place.
// So, ¯\(ツ)/¯ again.
//
// Based on this, QRandomGenerator::system() wins by a tiny non-functional
// margin; my somewhat educated opinion for now is that both APIs are
// equally good from the functionality and security point of view.

// QRandomGenerator::fillRange works in terms of 32-bit words,
// and FixedBuffer happens to deal with sizes that are multiples
// of those (16, 32, etc.)
const qsizetype wordsCount = bytes.size() / 4;
QRandomGenerator::system()->fillRange(std::bit_cast<uint32_t*>(bytes.data()), wordsCount);
if (const int remainder = bytes.size() % 4; Q_UNLIKELY(remainder != 0)) {
// Not normal; but if it happens, apply best effort
QRandomGenerator::system()->generate(bytes.end() - remainder,
bytes.end());
}
}

namespace {
auto initializeSecureHeap()
{
#if !defined(LIBRESSL_VERSION_NUMBER)
const auto result =
CRYPTO_secure_malloc_init(FixedBufferBase::TotalSecureHeapSize, 16);
if (result > 0) {
qDebug(E2EE) << FixedBufferBase::TotalSecureHeapSize
<< "bytes of secure heap initialised";
if (std::atexit([] {
CRYPTO_secure_malloc_done();
qDebug(E2EE) << "Dismantled secure heap";
})
!= 0)
qWarning(E2EE)
<< "Could not register a cleanup function for secure heap!";
} else
qCritical(E2EE) << "Secure heap could not be initialised, sensitive "
"data will remain in common dynamic memory";
#else
const auto result = 0;
qCritical(E2EE) << "Secure heap is not available in LibreSSL";
#endif
return result;
}

FixedBufferBase::value_type* allocate(size_t bytes, bool initWithZeros = false)
{
#if !defined(LIBRESSL_VERSION_NUMBER)
static auto secureHeapInitialised [[maybe_unused]] = initializeSecureHeap();

const auto p = static_cast<FixedBufferBase::value_type*>(
initWithZeros ? OPENSSL_secure_zalloc(bytes) : OPENSSL_secure_malloc(bytes));
Q_ASSERT(CRYPTO_secure_allocated(p));
qDebug(E2EE) << "Allocated" << CRYPTO_secure_actual_size(p)
<< "bytes of secure heap (requested" << bytes << "bytes),"
<< CRYPTO_secure_used()
<< "/ 65536 bytes of secure heap used in total";
#else
const auto p =
static_cast<FixedBufferBase::value_type*>(initWithZeros ? calloc(bytes, 1) : malloc(bytes));
#endif
return p;
}

} // namespace

FixedBufferBase::FixedBufferBase(size_type bufferSize, InitOptions options)
: size_(bufferSize)
{
if (bufferSize >= TotalSecureHeapSize) {
qCritical(E2EE) << "Too large buffer size:" << bufferSize;
return;
}
if (options == Uninitialized)
return;

data_ = allocate(bufferSize, options == FillWithZeros);
if (options == FillWithRandom)
fillFromSecureRng({ data_, bufferSize });
}

void FixedBufferBase::fillFrom(QByteArray&& source)
{
if (unsignedSize(source) != size_) {
qCritical(E2EE) << "Can't load a fixed buffer of length" << size_
<< "from a string with length" << source.size();
Q_ASSERT(unsignedSize(source) == size_); // Always false
return;
}
if (data_ != nullptr) {
qWarning(E2EE) << "Overwriting the fixed buffer with another string";
clear();
}

data_ = allocate(size_);
std::copy(source.cbegin(), source.cend(), std::bit_cast<char*>(data_));
if (source.isDetached())
source.clear();
else
qWarning(E2EE)
<< "The fixed buffer source is shared; assuming that the caller is "
"responsible for securely clearing other copies";
}

void FixedBufferBase::clear()
{
if (empty())
return;

#if !defined(LIBRESSL_VERSION_NUMBER)
Q_ASSERT(CRYPTO_secure_allocated(data_));
const auto actualSize = OPENSSL_secure_actual_size(data_);
OPENSSL_secure_clear_free(data_, size_);
qDebug(E2EE) << "Deallocated" << actualSize << "bytes,"
<< CRYPTO_secure_used() << "/ 65536 bytes of secure heap used";
#else
free(data_);
#endif
data_ = nullptr;
}

0 comments on commit 52d988a

Please sign in to comment.