Coverage Report

Created: 2025-06-22 06:27

/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
0
{
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
0
    static std::unique_ptr<QUtil::FileCloser> random_device = []() {
93
0
        FILE* f = fopen("/dev/urandom", "rb");
94
0
        if (f == nullptr) {
95
0
            f = fopen("/dev/arandom", "rb");
96
0
        }
97
0
        if (f == nullptr) {
98
0
            f = fopen("/dev/random", "rb");
99
0
        }
100
0
        if (f == nullptr) {
101
0
            throw std::runtime_error("unable to find device in /dev for generating random numbers");
102
0
        }
103
0
        return std::make_unique<QUtil::FileCloser>(f);
104
0
    }();
105
106
0
    size_t fr = fread(data, 1, len, random_device->f);
107
0
    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
0
# endif
113
0
}
114
115
RandomDataProvider*
116
SecureRandomDataProvider::getInstance()
117
0
{
118
0
    static SecureRandomDataProvider instance;
119
0
    return &instance;
120
0
}
121
122
#endif