2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-09-20 09:09:06 +00:00
qpdf/libqpdf/SecureRandomDataProvider.cc
Jay Berkenbilt d71f05ca07 Fix sign and conversion warnings (major)
This makes all integer type conversions that have potential data loss
explicit with calls that do range checks and raise an exception. After
this commit, qpdf builds with no warnings when -Wsign-conversion
-Wconversion is used with gcc or clang or when -W3 -Wd4800 is used
with MSVC. This significantly reduces the likelihood of potential
crashes from bogus integer values.

There are some parts of the code that take int when they should take
size_t or an offset. Such places would make qpdf not support files
with more than 2^31 of something that usually wouldn't be so large. In
the event that such a file shows up and is valid, at least qpdf would
raise an error in the right spot so the issue could be legitimately
addressed rather than failing in some weird way because of a silent
overflow condition.
2019-06-21 13:17:21 -04:00

136 lines
3.5 KiB
C++

#include <qpdf/SecureRandomDataProvider.hh>
#include <qpdf/qpdf-config.h>
#include <qpdf/QUtil.hh>
#ifdef _WIN32
# include <windows.h>
# include <direct.h>
# include <io.h>
# ifndef SKIP_OS_SECURE_RANDOM
# include <wincrypt.h>
# endif
#endif
SecureRandomDataProvider::SecureRandomDataProvider()
{
}
SecureRandomDataProvider::~SecureRandomDataProvider()
{
}
#ifdef SKIP_OS_SECURE_RANDOM
void
SecureRandomDataProvider::provideRandomData(unsigned char* data, size_t len)
{
throw std::logic_error("SecureRandomDataProvider::provideRandomData called when support was not compiled in");
}
RandomDataProvider*
SecureRandomDataProvider::getInstance()
{
return 0;
}
#else
#ifdef _WIN32
class WindowsCryptProvider
{
public:
WindowsCryptProvider()
{
if (!CryptAcquireContext(&crypt_prov,
"Container",
NULL,
PROV_RSA_FULL,
0))
{
#if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || \
defined(__clang__))
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wold-style-cast"
# pragma GCC diagnostic ignored "-Wsign-compare"
# pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
if (GetLastError() == NTE_BAD_KEYSET)
#if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || \
defined(__clang__))
# pragma GCC diagnostic pop
#endif
{
if (! CryptAcquireContext(&crypt_prov,
"Container",
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))
{
throw std::runtime_error(
"unable to acquire crypt context with new keyset");
}
}
else
{
throw std::runtime_error("unable to acquire crypt context");
}
}
}
~WindowsCryptProvider()
{
// Ignore error
CryptReleaseContext(crypt_prov, 0);
}
HCRYPTPROV crypt_prov;
};
#endif
void
SecureRandomDataProvider::provideRandomData(unsigned char* data, size_t len)
{
#if defined(_WIN32)
// Optimization: make the WindowsCryptProvider static as long as
// it can be done in a thread-safe fashion.
WindowsCryptProvider c;
if (! CryptGenRandom(c.crypt_prov, static_cast<DWORD>(len),
reinterpret_cast<BYTE*>(data)))
{
throw std::runtime_error("unable to generate secure random data");
}
#elif defined(RANDOM_DEVICE)
// Optimization: wrap the file open and close in a class so that
// the file is closed in a destructor, then make this static to
// keep the file handle open. Only do this if it can be done in a
// thread-safe fashion.
FILE* f = QUtil::safe_fopen(RANDOM_DEVICE, "rb");
size_t fr = fread(data, 1, len, f);
fclose(f);
if (fr != len)
{
throw std::runtime_error(
"unable to read " +
QUtil::uint_to_string(len) +
" bytes from " + std::string(RANDOM_DEVICE));
}
#else
# error "Don't know how to generate secure random numbers on this platform. See random number generation in the top-level README.md"
#endif
}
RandomDataProvider*
SecureRandomDataProvider::getInstance()
{
static SecureRandomDataProvider instance;
return &instance;
}
#endif