aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf/SecureRandomDataProvider.cc
blob: e5007d627ce5239c128c74ae461ee21bb55ba08c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <qpdf/SecureRandomDataProvider.hh>

#include <qpdf/QUtil.hh>
#include <qpdf/qpdf-config.h>
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <direct.h>
# include <io.h>
# include <windows.h>
# ifndef SKIP_OS_SECURE_RANDOM
#  include <wincrypt.h>
# endif
#endif

#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

namespace
{
    class WindowsCryptProvider
    {
      public:
        WindowsCryptProvider()
        {
            if (!CryptAcquireContextW(
                    &crypt_prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
                throw std::runtime_error("unable to acquire crypt context: " + getErrorMessage());
            }
        }
        ~WindowsCryptProvider()
        {
            // Ignore error
            CryptReleaseContext(crypt_prov, 0);
        }

        HCRYPTPROV crypt_prov;

      private:
        std::string
        getErrorMessage()
        {
            DWORD errorMessageID = ::GetLastError();
            LPSTR messageBuffer = nullptr;
            size_t size = FormatMessageA(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
                    FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                errorMessageID,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                reinterpret_cast<LPSTR>(&messageBuffer),
                0,
                NULL);
            std::string message(messageBuffer, size);
            LocalFree(messageBuffer);
            return (
                "error number " + QUtil::int_to_string_base(errorMessageID, 16) + ": " + message);
        }
    };
} // namespace
# 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");
    }

# else
    static std::unique_ptr<QUtil::FileCloser> random_device = []() {
        FILE* f = fopen("/dev/urandom", "rb");
        if (f == nullptr) {
            f = fopen("/dev/arandom", "rb");
        }
        if (f == nullptr) {
            f = fopen("/dev/random", "rb");
        }
        if (f == nullptr) {
            throw std::runtime_error("unable to find device in /dev for generating random numbers");
        }
        return std::make_unique<QUtil::FileCloser>(f);
    }();

    size_t fr = fread(data, 1, len, random_device->f);
    if (fr != len) {
        throw std::runtime_error(
            "unable to read " + std::to_string(len) + " bytes from random number device");
    }

# endif
}

RandomDataProvider*
SecureRandomDataProvider::getInstance()
{
    static SecureRandomDataProvider instance;
    return &instance;
}

#endif