From 30287d2d655e1a9fe476477b6c74b62f816f37d6 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 30 Nov 2013 12:25:01 -0500 Subject: [PATCH] Allow OS-provided secure random to be disabled --- ChangeLog | 7 +++++ README | 46 ++++++++++++++++++++++------- configure.ac | 19 ++++++++++-- libqpdf/SecureRandomDataProvider.cc | 18 +++++++++++ libtests/random.cc | 3 ++ manual/qpdf-manual.xml | 25 ++++++++++++++++ 6 files changed, 105 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index 84b8297c..fc880375 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,13 @@ OS-provided or insecure random number generation. See documentation for 5.1.0 for details. + * Add configure option --enable-os-secure-random (enabled by + default). Pass --disable-os-secure-random or define + SKIP_OS_SECURE_RANDOM to avoid attempts to use the operating + system-provided secure random number generation. This can be + especially useful on Windows if you wish to avoid any dependency + on Microsoft's cryptography system. + 2013-11-29 Jay Berkenbilt * If NO_GET_ENVIRONMENT is #defined, for Windows only, diff --git a/README b/README index 5373a61e..8f394a1c 100644 --- a/README +++ b/README @@ -196,17 +196,41 @@ packages for qpdf enable this option, for example. Random Number Generation ======================== -When the qpdf detects either the Windows cryptography API or the -existence of /dev/urandom, /dev/arandom, or /dev/random, it uses them -to generate cryptography secure random numbers. If none of these -conditions are true, the build will fail with an error. It is -possible to configure qpdf with the --enable-insecure-random option, -in which case it will generate random numbers with stdlib's random() -or rand() calls instead. These random numbers are not cryptography -secure, but the qpdf library is fully functional using them. Using -non-secure random numbers means that it's easier in some cases to -guess encryption keys. If you're not generating encrypted files, -there's no advantage to using secure random numbers. +By default, when the qpdf detects either the Windows cryptography API +or the existence of /dev/urandom, /dev/arandom, or /dev/random, it +uses them to generate cryptography secure random numbers. If none of +these conditions are true, the build will fail with an error. This +behavior can be modified in several ways: + + * If you configure with --disable-os-secure-random or define + SKIP_OS_SECURE_RANDOM, qpdf will not attempt to use Windows + cryptography or the random device. You must either supply your own + random data provider or allow use of insecure random numbers. + + * If you configure qpdf with the --enable-insecure-random option or + define USE_INSECURE_RANDOM, qpdf will try insecure random numbers + if OS-provided secure random numbers are disabled. This is not a + fallback. In order for insecure random numbers to be used, you + must also disable OS secure random numbers since, otherwise, + failure to find OS secure random numbers is a compile error. The + insecure random number source is stdlib's random() or rand() calls. + These random numbers are not cryptography secure, but the qpdf + library is fully functional using them. Using non-secure random + numbers means that it's easier in some cases to guess encryption + keys. If you're not generating encrypted files, there's no + advantage to using secure random numbers. + + * In all cases, you may supply your own random data provider. To do + this, derive a class from qpdf/RandomDataProvider (since 5.1.0) and + call QUtil::setRandomDataProvider before you create any QPDF + objects. If you supply your own random data provider, it will + always be used even if support for one of the other random data + providers is compiled in. If you wish to avoid any possibility of + your build of qpdf from using anything but a user-supplied random + data provider, you can define SKIP_OS_SECURE_RANDOM and not + USE_INSECURE_RANDOM. In this case, qpdf will throw a runtime error + if any attempt is made to generate random numbers and no random + data provider has been supplied. If you are building qpdf on a platform that qpdf doesn't know how to generate secure random numbers on, a patch would be welcome. diff --git a/configure.ac b/configure.ac index 683b239f..9b53403b 100644 --- a/configure.ac +++ b/configure.ac @@ -26,11 +26,26 @@ AC_ARG_ENABLE(insecure-random, fi], [qpdf_INSECURE_RANDOM=0]) if test "$qpdf_INSECURE_RANDOM" = "1"; then AC_MSG_RESULT(yes) - AC_DEFINE([USE_INSECURE_RANDOM], [1], [Whether to use inscure random numbers]) + AC_DEFINE([USE_INSECURE_RANDOM], [1], [Whether to use insecure random numbers]) else AC_MSG_RESULT(no) fi +AC_ARG_ENABLE(os-secure-random, + AS_HELP_STRING([--enable-os-secure-random], + [whether to try to use OS-provided secure random numbers (default is yes)]), + [if test "$enableval" = "yes"; then + qpdf_OS_SECURE_RANDOM=1; + else + qpdf_OS_SECURE_RANDOM=0; + fi], [qpdf_OS_SECURE_RANDOM=1]) +if test "$qpdf_OS_SECURE_RANDOM" = "1"; then + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) + AC_DEFINE([SKIP_OS_SECURE_RANDOM], [1], [Whether to suppres use of OS-provided secure random numbers]) +fi + AX_RANDOM_DEVICE USE_EXTERNAL_LIBS=0 @@ -71,7 +86,7 @@ if test "$BUILD_INTERNAL_LIBS" = "0"; then AC_SEARCH_LIBS(pcre_compile,pcre,,[MISSING_PCRE=1; MISSING_ANY=1]) fi -if test "x$qpdf_INSECURE_RANDOM" != "x1"; then +if test "x$qpdf_OS_SECURE_RANDOM" = "x1"; then OLIBS=$LIBS LIBS="$LIBS Advapi32.lib" AC_MSG_CHECKING(for Advapi32 library) diff --git a/libqpdf/SecureRandomDataProvider.cc b/libqpdf/SecureRandomDataProvider.cc index 14ef55a7..2870ab98 100644 --- a/libqpdf/SecureRandomDataProvider.cc +++ b/libqpdf/SecureRandomDataProvider.cc @@ -19,6 +19,22 @@ 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 @@ -84,3 +100,5 @@ SecureRandomDataProvider::getInstance() static SecureRandomDataProvider instance; return &instance; } + +#endif diff --git a/libtests/random.cc b/libtests/random.cc index 644fdd91..0099844b 100644 --- a/libtests/random.cc +++ b/libtests/random.cc @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -36,6 +37,7 @@ int main() { std::cout << "fail: two insecure randoms were the same\n"; } +#ifndef SKIP_OS_SECURE_RANDOM SecureRandomDataProvider srdp; srdp.provideRandomData(reinterpret_cast(&r1), 4); srdp.provideRandomData(reinterpret_cast(&r2), 4); @@ -43,6 +45,7 @@ int main() { std::cout << "fail: two secure randoms were the same\n"; } +#endif BogusRandomDataProvider brdp; QUtil::setRandomDataProvider(&brdp); r1 = QUtil::random(); diff --git a/manual/qpdf-manual.xml b/manual/qpdf-manual.xml index 8d68db9d..2cb6be5d 100644 --- a/manual/qpdf-manual.xml +++ b/manual/qpdf-manual.xml @@ -1851,6 +1851,31 @@ outfile.pdf preserved as clear-text if it is that way in the original file. + + Random Number Generation + + QPDF generates random numbers to support generation of encrypted + data. Versions prior to 5.0.1 used random or + rand from stdlib to + generate random numbers. Version 5.0.1, if available, used + operating system-provided secure random number generation instead, + enabling use of stdlib random number + generation only if enabled by a compile-time option. Starting in + version 5.1.0, use of insecure random numbers was disabled unless + enabled at compile time. Starting in version 5.1.0, it is also + possible for you to disable use of OS-provided secure random + numbers. This is especially useful on Windows if you want to + avoid a dependency on Microsoft's cryptography API. In this case, + you must provide your own random data provider. Regardless of how + you compile qpdf, starting in version 5.1.0, it is possible for + you to provide your own random data provider at runtime. This + would enable you to use some software-based secure pseudorandom + number generator and to avoid use of whatever the operating system + provides. For details on how to do this, please refer to the + top-level README file in the source distribution and to comments + in QUtil.hh. + + Adding and Removing Pages