Skip to content

Commit f20d779

Browse files
committed
[core] make TSystem::GetCryptoRandom() static
Factor out calling the OS crypto random number generator into a free function in the Base package. In turn, TSystem::GetCryptoRandom() can be static and does not rely in gSystem being initialized.
1 parent b387638 commit f20d779

13 files changed

Lines changed: 198 additions & 152 deletions

core/base/CMakeLists.txt

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ if(MSVC AND MSVC_VERSION GREATER_EQUAL 1925 AND MSVC_VERSION LESS 1929)
1616
endif()
1717

1818
set(BASE_HEADERS
19+
ROOT/RCryptoRandom.hxx
1920
ROOT/RFloat16.hxx
2021
ROOT/TErrorDefaultHandler.hxx
2122
ROOT/TExecutorCRTP.hxx
@@ -118,6 +119,7 @@ set(BASE_HEADERS
118119

119120
set(BASE_SOURCES
120121
src/Match.cxx
122+
src/RCryptoRandom.cxx
121123
src/String.cxx
122124
src/Stringio.cxx
123125
src/TApplication.cxx
@@ -288,3 +290,70 @@ generateManual(rootclingMan ${CMAKE_SOURCE_DIR}/core/dictgen/src/rootcling-argpa
288290
endif()
289291

290292
ROOT_ADD_TEST_SUBDIRECTORY(test)
293+
294+
if(UNIX)
295+
# Determine cryptographic random number generator
296+
297+
CHECK_CXX_SOURCE_COMPILES("#include <cstdlib>
298+
int main() { char buf[32]; arc4random_buf(buf, 32); return 0;}" found_arc4)
299+
300+
if(found_arc4)
301+
message(STATUS "Found arc4random_buf in stdlib.h")
302+
target_compile_definitions(Core PRIVATE R__ARC4_STDLIB)
303+
else()
304+
set(OLD_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES})
305+
set(OLD_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
306+
if(DEFINED LIBBSDROOT)
307+
set(CMAKE_REQUIRED_INCLUDES ${LIBBSDROOT}/include)
308+
set(CMAKE_REQUIRED_LIBRARIES ${LIBBSDROOT}/lib/libbsd.so)
309+
endif()
310+
CHECK_CXX_SOURCE_COMPILES("#include <bsd/stdlib.h>
311+
int main() { char buf[32]; arc4random_buf(buf, 32); return 0;}" found_arc4_bsd)
312+
set(CMAKE_REQUIRED_INCLUDES ${OLD_CMAKE_REQUIRED_INCLUDES})
313+
set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQUIRED_LIBRARIES})
314+
if(found_arc4_bsd)
315+
message(STATUS "Found arc4random_buf in bsd/stdlib.h")
316+
target_compile_definitions(Core PRIVATE R__ARC4_BSDLIB)
317+
if(DEFINED LIBBSDROOT)
318+
target_include_directories(Core PRIVATE ${LIBBSDROOT}/include)
319+
target_link_libraries(Core PRIVATE ${LIBBSDROOT}/lib/libbsd.so)
320+
endif()
321+
else()
322+
CHECK_CXX_SOURCE_COMPILES("#include <sys/random.h>
323+
int main() { char buf[32]; int res = getrandom(buf, 32, GRND_NONBLOCK); return 0;}" found_getrandom)
324+
if(found_getrandom)
325+
message(STATUS "Found getrandom in sys/random.h")
326+
target_compile_definitions(Core PRIVATE R__GETRANDOM_CLIB)
327+
else()
328+
CHECK_CXX_SOURCE_RUNS("
329+
#include <fstream>
330+
331+
int main() {
332+
std::ifstream urandom{\"/dev/urandom\"};
333+
if (!urandom) {
334+
// This will make the CMake command fail
335+
return 1;
336+
}
337+
338+
constexpr int len{32};
339+
char buf[len];
340+
for (int n = 0; n < len; n++) buf[n] = 0;
341+
urandom.read(buf, len);
342+
343+
int nmatch = 0;
344+
for (int n = 0; n < len; n++)
345+
if (buf[n] == 0) nmatch++;
346+
347+
// Fail if no values have changed
348+
return nmatch != len ? 0 : 1;
349+
}" found_urandom)
350+
if(found_urandom)
351+
message(STATUS "Found random device in /dev/urandom")
352+
target_compile_definitions(Core PRIVATE R__USE_URANDOM)
353+
else()
354+
message(FATAL_ERROR "Fail to detect cryptographic random generator")
355+
endif()
356+
endif()
357+
endif()
358+
endif()
359+
endif(UNIX)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/// \file ROOT/RCryptoRandom.hxx
2+
/// \ingroup Base
3+
/// \date 2026-04-24
4+
5+
/*************************************************************************
6+
* Copyright (C) 1995-2026, Rene Brun and Fons Rademakers. *
7+
* All rights reserved. *
8+
* *
9+
* For the licensing terms see $ROOTSYS/LICENSE. *
10+
* For the list of contributors see $ROOTSYS/README/CREDITS. *
11+
*************************************************************************/
12+
13+
#ifndef ROOT_RCryptoRandom
14+
#define ROOT_RCryptoRandom
15+
16+
namespace ROOT {
17+
namespace Internal {
18+
19+
/// Get random bytes from the operating system's cryptographic random number generator
20+
/// The requested number of bytes must not exceed 256.
21+
bool GetCryptoRandom(void *buf, unsigned int len);
22+
23+
} // namespace Internal
24+
} // namespace ROOT
25+
26+
#endif

core/base/inc/TSystem.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ class TSystem : public TNamed {
369369
void SetErrorStr(const char *errstr);
370370
const char *GetErrorStr() const { return GetLastErrorString(); }
371371
virtual const char *GetError();
372-
virtual Int_t GetCryptoRandom(void *buf, Int_t len);
372+
static Int_t GetCryptoRandom(void *buf, Int_t len);
373373
void RemoveOnExit(TObject *obj);
374374
virtual const char *HostName();
375375
virtual void NotifyApplicationCreated();

core/base/src/RCryptoRandom.cxx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include "ROOT/RConfig.hxx"
2+
#include "ROOT/RCryptoRandom.hxx"
3+
4+
#ifdef WIN32
5+
#include "Windows4Root.h"
6+
#include <bcrypt.h>
7+
#else
8+
#if defined(R__ARC4_STDLIB)
9+
#include <cstdlib>
10+
#elif defined(R__ARC4_BSDLIB)
11+
#include <bsd/stdlib.h>
12+
#elif defined(R__GETRANDOM_CLIB)
13+
#include <sys/random.h>
14+
#elif defined(R__USE_URANDOM)
15+
#include <fstream>
16+
#endif
17+
#endif
18+
19+
bool ROOT::Internal::GetCryptoRandom(void *buf, unsigned int len)
20+
{
21+
if (len > 256)
22+
return false;
23+
24+
#ifdef WIN32
25+
26+
return BCryptGenRandom((BCRYPT_ALG_HANDLE)NULL, (PUCHAR)buf, (ULONG)len, BCRYPT_USE_SYSTEM_PREFERRED_RNG) == 0;
27+
28+
#else // UNIX
29+
30+
#if defined(R__ARC4_STDLIB) || defined(R__ARC4_BSDLIB)
31+
arc4random_buf(buf, len);
32+
return true;
33+
#elif defined(R__GETRANDOM_CLIB)
34+
return getrandom(buf, len, GRND_NONBLOCK) == len;
35+
#elif defined(R__USE_URANDOM)
36+
std::ifstream urandom{"/dev/urandom"};
37+
if (!urandom)
38+
return false;
39+
urandom.read(reinterpret_cast<char *>(buf), len);
40+
return urandom.good();
41+
#else
42+
#error "Reliable cryptographic random function not defined"
43+
return false;
44+
#endif
45+
46+
#endif
47+
}

core/base/src/TSystem.cxx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ allows a simple partial implementation for new OS'es.
2222
*/
2323

2424
#include <ROOT/FoundationUtils.hxx>
25+
#include <ROOT/RCryptoRandom.hxx>
2526
#include "strlcpy.h"
2627
#include "TSystem.h"
2728
#include "TApplication.h"
@@ -258,13 +259,16 @@ const char *TSystem::GetError()
258259

259260
////////////////////////////////////////////////////////////////////////////////
260261
/// Return cryptographic random number
261-
/// Fill provided buffer with random values
262+
/// Fill provided buffer with random values. The number of requested random bytes must not exceed 256.
262263
/// Returns number of bytes written to buffer or -1 in case of error
263264

264-
Int_t TSystem::GetCryptoRandom(void * /* buf */, Int_t /* len */)
265+
Int_t TSystem::GetCryptoRandom(void *buf, Int_t len)
265266
{
266-
Error("GetCryptoRandom", "Not implemented");
267-
return -1;
267+
if (len < 0) {
268+
return -1;
269+
}
270+
const auto rv = ROOT::Internal::GetCryptoRandom(buf, len);
271+
return rv ? len : -1;
268272
}
269273

270274

core/base/test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ ROOT_ADD_GTEST(CoreErrorTests TErrorTests.cxx LIBRARIES Core)
2626

2727
ROOT_ADD_GTEST(CoreSystemTests TSystemTests.cxx LIBRARIES Core)
2828

29+
ROOT_ADD_GTEST(CoreCryptoRandomTest CryptoRandomTest.cxx LIBRARIES Core)
30+
2931
ROOT_ADD_GTEST(CoreColorTests TColorTests.cxx LIBRARIES Core)
3032
if(CMAKE_SYSTEM_NAME MATCHES "Darwin" AND CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 25.4)
3133
set_tests_properties(gtest-core-base-CoreColorTests PROPERTIES DISABLED True)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include "gtest/gtest.h"
2+
3+
#include <ROOT/RCryptoRandom.hxx>
4+
5+
TEST(TSystem, CryptoRandom)
6+
{
7+
// test with 512 bits, longer keys may not work
8+
9+
const int len = 64;
10+
uint8_t buf[64];
11+
12+
for (int n = 0; n < len; n++)
13+
buf[n] = 0;
14+
15+
EXPECT_TRUE(ROOT::Internal::GetCryptoRandom(buf, len));
16+
17+
int nmatch = 0;
18+
19+
for (int n = 0; n < len; n++)
20+
if (buf[n] == 0)
21+
nmatch++;
22+
23+
// check that values in buffer changed
24+
EXPECT_TRUE(nmatch != len);
25+
26+
for (int n = 0; n < len; n++)
27+
buf[n] = n;
28+
29+
EXPECT_TRUE(ROOT::Internal::GetCryptoRandom(buf, len));
30+
31+
nmatch = 0;
32+
33+
for (int n = 0; n < len; n++)
34+
if (buf[n] == n)
35+
nmatch++;
36+
37+
// check that values in buffer changed
38+
EXPECT_TRUE(nmatch != len);
39+
}

core/base/test/TSystemTests.cxx

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -56,43 +56,3 @@ TEST(TSystem, TempFileSuffix)
5656

5757
gSystem->Unlink(fname);
5858
}
59-
60-
TEST(TSystem, CryptoRandom)
61-
{
62-
// test with 512 bits, longer keys may not work
63-
64-
const int len = 64;
65-
uint8_t buf[64];
66-
67-
for (int n = 0; n < len; n++)
68-
buf[n] = 0;
69-
70-
auto res = gSystem->GetCryptoRandom(buf, len);
71-
72-
EXPECT_EQ(res, len);
73-
74-
int nmatch = 0;
75-
76-
for (int n = 0; n < len; n++)
77-
if (buf[n] == 0)
78-
nmatch++;
79-
80-
// check that values in buffer changed
81-
EXPECT_TRUE(nmatch != len);
82-
83-
for (int n = 0; n < len; n++)
84-
buf[n] = n;
85-
86-
res = gSystem->GetCryptoRandom(buf, len);
87-
88-
EXPECT_EQ(res, len);
89-
90-
nmatch = 0;
91-
92-
for (int n = 0; n < len; n++)
93-
if (buf[n] == n)
94-
nmatch++;
95-
96-
// check that values in buffer changed
97-
EXPECT_TRUE(nmatch != len);
98-
}

core/unix/CMakeLists.txt

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -20,68 +20,4 @@ if (CMAKE_SYSTEM_NAME MATCHES FreeBSD)
2020
target_link_libraries(Core PRIVATE execinfo util)
2121
endif()
2222

23-
CHECK_CXX_SOURCE_COMPILES("#include <cstdlib>
24-
int main() { char buf[32]; arc4random_buf(buf, 32); return 0;}" found_arc4)
25-
26-
if(found_arc4)
27-
message(STATUS "Found arc4random_buf in stdlib.h")
28-
target_compile_definitions(Core PRIVATE R__ARC4_STDLIB)
29-
else()
30-
set(OLD_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES})
31-
set(OLD_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
32-
if(DEFINED LIBBSDROOT)
33-
set(CMAKE_REQUIRED_INCLUDES ${LIBBSDROOT}/include)
34-
set(CMAKE_REQUIRED_LIBRARIES ${LIBBSDROOT}/lib/libbsd.so)
35-
endif()
36-
CHECK_CXX_SOURCE_COMPILES("#include <bsd/stdlib.h>
37-
int main() { char buf[32]; arc4random_buf(buf, 32); return 0;}" found_arc4_bsd)
38-
set(CMAKE_REQUIRED_INCLUDES ${OLD_CMAKE_REQUIRED_INCLUDES})
39-
set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQUIRED_LIBRARIES})
40-
if(found_arc4_bsd)
41-
message(STATUS "Found arc4random_buf in bsd/stdlib.h")
42-
target_compile_definitions(Core PRIVATE R__ARC4_BSDLIB)
43-
if(DEFINED LIBBSDROOT)
44-
target_include_directories(Core PRIVATE ${LIBBSDROOT}/include)
45-
target_link_libraries(Core PRIVATE ${LIBBSDROOT}/lib/libbsd.so)
46-
endif()
47-
else()
48-
CHECK_CXX_SOURCE_COMPILES("#include <sys/random.h>
49-
int main() { char buf[32]; int res = getrandom(buf, 32, GRND_NONBLOCK); return 0;}" found_getrandom)
50-
if(found_getrandom)
51-
message(STATUS "Found getrandom in sys/random.h")
52-
target_compile_definitions(Core PRIVATE R__GETRANDOM_CLIB)
53-
else()
54-
CHECK_CXX_SOURCE_RUNS("
55-
#include <fstream>
56-
57-
int main() {
58-
std::ifstream urandom{\"/dev/urandom\"};
59-
if (!urandom) {
60-
// This will make the CMake command fail
61-
return 1;
62-
}
63-
64-
constexpr int len{32};
65-
char buf[len];
66-
for (int n = 0; n < len; n++) buf[n] = 0;
67-
urandom.read(buf, len);
68-
69-
int nmatch = 0;
70-
for (int n = 0; n < len; n++)
71-
if (buf[n] == 0) nmatch++;
72-
73-
// Fail if no values have changed
74-
return nmatch != len ? 0 : 1;
75-
}" found_urandom)
76-
if(found_urandom)
77-
message(STATUS "Found random device in /dev/urandom")
78-
target_compile_definitions(Core PRIVATE R__USE_URANDOM)
79-
else()
80-
message(FATAL_ERROR "Fail to detect cryptographic random generator")
81-
endif()
82-
endif()
83-
endif()
84-
endif()
85-
86-
8723
ROOT_INSTALL_HEADERS()

core/unix/inc/TUnixSystem.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ class TUnixSystem : public TSystem {
7878
void SetProgname(const char *name) override;
7979
void SetDisplay() override;
8080
const char *GetError() override;
81-
Int_t GetCryptoRandom(void *buf, Int_t len) override;
8281
const char *HostName() override;
8382

8483
//---- EventLoop --------------------------------------------

0 commit comments

Comments
 (0)