/src/qpdf/libqpdf/SecureRandomDataProvider.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include <qpdf/SecureRandomDataProvider.hh> |
2 | | |
3 | | #include <qpdf/QUtil.hh> |
4 | | #include <qpdf/qpdf-config.h> |
5 | | #ifdef _WIN32 |
6 | | # define WIN32_LEAN_AND_MEAN |
7 | | # include <direct.h> |
8 | | # include <io.h> |
9 | | # include <windows.h> |
10 | | # ifndef SKIP_OS_SECURE_RANDOM |
11 | | # include <wincrypt.h> |
12 | | # endif |
13 | | #endif |
14 | | |
15 | | #ifdef SKIP_OS_SECURE_RANDOM |
16 | | |
17 | | void |
18 | | SecureRandomDataProvider::provideRandomData(unsigned char* data, size_t len) |
19 | | { |
20 | | throw std::logic_error( |
21 | | "SecureRandomDataProvider::provideRandomData called " |
22 | | "when support was not compiled in"); |
23 | | } |
24 | | |
25 | | RandomDataProvider* |
26 | | SecureRandomDataProvider::getInstance() |
27 | | { |
28 | | return 0; |
29 | | } |
30 | | |
31 | | #else |
32 | | |
33 | | # ifdef _WIN32 |
34 | | |
35 | | namespace |
36 | | { |
37 | | class WindowsCryptProvider |
38 | | { |
39 | | public: |
40 | | WindowsCryptProvider() |
41 | | { |
42 | | if (!CryptAcquireContextW( |
43 | | &crypt_prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { |
44 | | throw std::runtime_error("unable to acquire crypt context: " + getErrorMessage()); |
45 | | } |
46 | | } |
47 | | ~WindowsCryptProvider() |
48 | | { |
49 | | // Ignore error |
50 | | CryptReleaseContext(crypt_prov, 0); |
51 | | } |
52 | | |
53 | | HCRYPTPROV crypt_prov; |
54 | | |
55 | | private: |
56 | | std::string |
57 | | getErrorMessage() |
58 | | { |
59 | | DWORD errorMessageID = ::GetLastError(); |
60 | | LPSTR messageBuffer = nullptr; |
61 | | size_t size = FormatMessageA( |
62 | | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
63 | | FORMAT_MESSAGE_IGNORE_INSERTS, |
64 | | NULL, |
65 | | errorMessageID, |
66 | | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
67 | | reinterpret_cast<LPSTR>(&messageBuffer), |
68 | | 0, |
69 | | NULL); |
70 | | std::string message(messageBuffer, size); |
71 | | LocalFree(messageBuffer); |
72 | | return ( |
73 | | "error number " + QUtil::int_to_string_base(errorMessageID, 16) + ": " + message); |
74 | | } |
75 | | }; |
76 | | } // namespace |
77 | | # endif |
78 | | |
79 | | void |
80 | | SecureRandomDataProvider::provideRandomData(unsigned char* data, size_t len) |
81 | 133k | { |
82 | | # if defined(_WIN32) |
83 | | |
84 | | // Optimization: make the WindowsCryptProvider static as long as |
85 | | // it can be done in a thread-safe fashion. |
86 | | WindowsCryptProvider c; |
87 | | if (!CryptGenRandom(c.crypt_prov, static_cast<DWORD>(len), reinterpret_cast<BYTE*>(data))) { |
88 | | throw std::runtime_error("unable to generate secure random data"); |
89 | | } |
90 | | |
91 | | # else |
92 | 133k | static std::unique_ptr<QUtil::FileCloser> random_device = []() { |
93 | 1 | FILE* f = fopen("/dev/urandom", "rb"); |
94 | 1 | if (f == nullptr) { |
95 | 0 | f = fopen("/dev/arandom", "rb"); |
96 | 0 | } |
97 | 1 | if (f == nullptr) { |
98 | 0 | f = fopen("/dev/random", "rb"); |
99 | 0 | } |
100 | 1 | if (f == nullptr) { |
101 | 0 | throw std::runtime_error("unable to find device in /dev for generating random numbers"); |
102 | 0 | } |
103 | 1 | return std::make_unique<QUtil::FileCloser>(f); |
104 | 1 | }(); |
105 | | |
106 | 133k | size_t fr = fread(data, 1, len, random_device->f); |
107 | 133k | if (fr != len) { |
108 | 0 | throw std::runtime_error( |
109 | 0 | "unable to read " + std::to_string(len) + " bytes from random number device"); |
110 | 0 | } |
111 | | |
112 | 133k | # endif |
113 | 133k | } |
114 | | |
115 | | RandomDataProvider* |
116 | | SecureRandomDataProvider::getInstance() |
117 | 1 | { |
118 | 1 | static SecureRandomDataProvider instance; |
119 | 1 | return &instance; |
120 | 1 | } |
121 | | |
122 | | #endif |