diff options
Diffstat (limited to 'libqpdf')
-rw-r--r-- | libqpdf/InsecureRandomDataProvider.cc | 49 | ||||
-rw-r--r-- | libqpdf/QUtil.cc | 114 | ||||
-rw-r--r-- | libqpdf/SecureRandomDataProvider.cc | 86 | ||||
-rw-r--r-- | libqpdf/build.mk | 2 | ||||
-rw-r--r-- | libqpdf/qpdf/InsecureRandomDataProvider.hh | 27 | ||||
-rw-r--r-- | libqpdf/qpdf/SecureRandomDataProvider.hh | 22 |
6 files changed, 221 insertions, 79 deletions
diff --git a/libqpdf/InsecureRandomDataProvider.cc b/libqpdf/InsecureRandomDataProvider.cc new file mode 100644 index 00000000..5f637746 --- /dev/null +++ b/libqpdf/InsecureRandomDataProvider.cc @@ -0,0 +1,49 @@ +#include <qpdf/InsecureRandomDataProvider.hh> + +#include <qpdf/qpdf-config.h> +#include <qpdf/QUtil.hh> +#include <stdlib.h> + +InsecureRandomDataProvider::InsecureRandomDataProvider() : + seeded_random(false) +{ +} + +InsecureRandomDataProvider::~InsecureRandomDataProvider() +{ +} + +void +InsecureRandomDataProvider::provideRandomData(unsigned char* data, size_t len) +{ + for (size_t i = 0; i < len; ++i) + { + data[i] = static_cast<unsigned char>((this->random() & 0xff0) >> 4); + } +} + +long +InsecureRandomDataProvider::random() +{ + if (! this->seeded_random) + { + // Seed the random number generator with something simple, but + // just to be interesting, don't use the unmodified current + // time. It would be better if this were a more secure seed. + QUtil::srandom(QUtil::get_current_time() ^ 0xcccc); + this->seeded_random = true; + } + +# ifdef HAVE_RANDOM + return ::random(); +# else + return rand(); +# endif +} + +RandomDataProvider* +InsecureRandomDataProvider::getInstance() +{ + static InsecureRandomDataProvider instance; + return &instance; +} diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc index 01e0b6e7..c5fe535c 100644 --- a/libqpdf/QUtil.cc +++ b/libqpdf/QUtil.cc @@ -3,6 +3,10 @@ #include <qpdf/QUtil.hh> #include <qpdf/PointerHolder.hh> +#ifdef USE_INSECURE_RANDOM +# include <qpdf/InsecureRandomDataProvider.hh> +#endif +#include <qpdf/SecureRandomDataProvider.hh> #include <cmath> #include <iomanip> @@ -18,7 +22,6 @@ #include <Windows.h> #include <direct.h> #include <io.h> -#include <Wincrypt.h> #else #include <unistd.h> #endif @@ -383,38 +386,7 @@ QUtil::toUTF8(unsigned long uval) return result; } -#ifdef USE_INSECURE_RANDOM - -long -QUtil::random() -{ - static bool seeded_random = false; - if (! seeded_random) - { - // Seed the random number generator with something simple, but - // just to be interesting, don't use the unmodified current - // time. It would be better if this were a more secure seed. - QUtil::srandom(QUtil::get_current_time() ^ 0xcccc); - seeded_random = true; - } - -# ifdef HAVE_RANDOM - return ::random(); -# else - return rand(); -# endif -} - -void -QUtil::initializeWithRandomBytes(unsigned char* data, size_t len) -{ - for (size_t i = 0; i < len; ++i) - { - data[i] = static_cast<unsigned char>((QUtil::random() & 0xff0) >> 4); - } -} - -#else +// Random data support long QUtil::random() @@ -426,66 +398,50 @@ QUtil::random() return result; } -#ifdef _WIN32 -class WindowsCryptProvider +static RandomDataProvider* random_data_provider = 0; + +#ifdef USE_INSECURE_RANDOM +static RandomDataProvider* insecure_random_data_provider = + InsecureRandomDataProvider::getInstance(); +#else +static RandomDataProvider* insecure_random_data_provider = 0; +#endif +static RandomDataProvider* secure_random_data_provider = + SecureRandomDataProvider::getInstance(); + +static void +initialize_random_data_provider() { - public: - WindowsCryptProvider() + if (random_data_provider == 0) { - if (! CryptAcquireContext(&crypt_prov, NULL, NULL, PROV_RSA_FULL, 0)) + if (secure_random_data_provider) + { + random_data_provider = secure_random_data_provider; + } + else if (insecure_random_data_provider) { - throw std::runtime_error("unable to acquire crypt context"); + random_data_provider = insecure_random_data_provider; } } - ~WindowsCryptProvider() + if (random_data_provider == 0) { - // Ignore error - CryptReleaseContext(crypt_prov, 0); + throw std::logic_error("QPDF has no random data provider"); } +} - HCRYPTPROV crypt_prov; -}; -#endif +void +QUtil::setRandomDataProvider(RandomDataProvider* p) +{ + random_data_provider = p; +} void QUtil::initializeWithRandomBytes(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, 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::int_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" - -#endif + initialize_random_data_provider(); + random_data_provider->provideRandomData(data, len); } -#endif - void QUtil::srandom(unsigned int seed) { diff --git a/libqpdf/SecureRandomDataProvider.cc b/libqpdf/SecureRandomDataProvider.cc new file mode 100644 index 00000000..14ef55a7 --- /dev/null +++ b/libqpdf/SecureRandomDataProvider.cc @@ -0,0 +1,86 @@ +#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 _WIN32 + +class WindowsCryptProvider +{ + public: + WindowsCryptProvider() + { + if (! CryptAcquireContext(&crypt_prov, NULL, NULL, PROV_RSA_FULL, 0)) + { + 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, 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::int_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" + +#endif +} + +RandomDataProvider* +SecureRandomDataProvider::getInstance() +{ + static SecureRandomDataProvider instance; + return &instance; +} diff --git a/libqpdf/build.mk b/libqpdf/build.mk index 20781230..4287b0de 100644 --- a/libqpdf/build.mk +++ b/libqpdf/build.mk @@ -11,6 +11,7 @@ SRCS_libqpdf = \ libqpdf/BufferInputSource.cc \ libqpdf/FileInputSource.cc \ libqpdf/InputSource.cc \ + libqpdf/InsecureRandomDataProvider.cc \ libqpdf/MD5.cc \ libqpdf/OffsetInputSource.cc \ libqpdf/PCRE.cc \ @@ -57,6 +58,7 @@ SRCS_libqpdf = \ libqpdf/QTC.cc \ libqpdf/QUtil.cc \ libqpdf/RC4.cc \ + libqpdf/SecureRandomDataProvider.cc \ libqpdf/qpdf-c.cc \ libqpdf/rijndael.cc \ libqpdf/sha2.c \ diff --git a/libqpdf/qpdf/InsecureRandomDataProvider.hh b/libqpdf/qpdf/InsecureRandomDataProvider.hh new file mode 100644 index 00000000..530ee3cb --- /dev/null +++ b/libqpdf/qpdf/InsecureRandomDataProvider.hh @@ -0,0 +1,27 @@ +#ifndef __INSECURERANDOMDATAPROVIDER_HH__ +#define __INSECURERANDOMDATAPROVIDER_HH__ + +#include <qpdf/RandomDataProvider.hh> +#include <qpdf/DLL.h> + +class InsecureRandomDataProvider: public RandomDataProvider +{ + public: + QPDF_DLL + InsecureRandomDataProvider(); + QPDF_DLL + virtual ~InsecureRandomDataProvider(); + + QPDF_DLL + virtual void provideRandomData(unsigned char* data, size_t len); + + QPDF_DLL + static RandomDataProvider* getInstance(); + + private: + long random(); + + bool seeded_random; +}; + +#endif // __INSECURERANDOMDATAPROVIDER_HH__ diff --git a/libqpdf/qpdf/SecureRandomDataProvider.hh b/libqpdf/qpdf/SecureRandomDataProvider.hh new file mode 100644 index 00000000..178c73c0 --- /dev/null +++ b/libqpdf/qpdf/SecureRandomDataProvider.hh @@ -0,0 +1,22 @@ +#ifndef __SECURERANDOMDATAPROVIDER_HH__ +#define __SECURERANDOMDATAPROVIDER_HH__ + +#include <qpdf/RandomDataProvider.hh> +#include <qpdf/DLL.h> + +class SecureRandomDataProvider: public RandomDataProvider +{ + public: + QPDF_DLL + SecureRandomDataProvider(); + QPDF_DLL + virtual ~SecureRandomDataProvider(); + + QPDF_DLL + virtual void provideRandomData(unsigned char* data, size_t len); + + QPDF_DLL + static RandomDataProvider* getInstance(); +}; + +#endif // __SECURERANDOMDATAPROVIDER_HH__ |