Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions core/base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ if(MSVC AND MSVC_VERSION GREATER_EQUAL 1925 AND MSVC_VERSION LESS 1929)
endif()

set(BASE_HEADERS
ROOT/RCryptoRandom.hxx
ROOT/RFloat16.hxx
ROOT/TErrorDefaultHandler.hxx
ROOT/TExecutorCRTP.hxx
Expand Down Expand Up @@ -118,6 +119,7 @@ set(BASE_HEADERS

set(BASE_SOURCES
src/Match.cxx
src/RCryptoRandom.cxx
src/String.cxx
src/Stringio.cxx
src/TApplication.cxx
Expand Down Expand Up @@ -288,3 +290,70 @@ generateManual(rootclingMan ${CMAKE_SOURCE_DIR}/core/dictgen/src/rootcling-argpa
endif()

ROOT_ADD_TEST_SUBDIRECTORY(test)

if(UNIX)
# Determine cryptographic random number generator

CHECK_CXX_SOURCE_COMPILES("#include <cstdlib>
int main() { char buf[32]; arc4random_buf(buf, 32); return 0;}" found_arc4)

if(found_arc4)
message(STATUS "Found arc4random_buf in stdlib.h")
target_compile_definitions(Core PRIVATE R__ARC4_STDLIB)
else()
set(OLD_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES})
set(OLD_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
if(DEFINED LIBBSDROOT)
set(CMAKE_REQUIRED_INCLUDES ${LIBBSDROOT}/include)
set(CMAKE_REQUIRED_LIBRARIES ${LIBBSDROOT}/lib/libbsd.so)
endif()
CHECK_CXX_SOURCE_COMPILES("#include <bsd/stdlib.h>
int main() { char buf[32]; arc4random_buf(buf, 32); return 0;}" found_arc4_bsd)
set(CMAKE_REQUIRED_INCLUDES ${OLD_CMAKE_REQUIRED_INCLUDES})
set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQUIRED_LIBRARIES})
if(found_arc4_bsd)
message(STATUS "Found arc4random_buf in bsd/stdlib.h")
target_compile_definitions(Core PRIVATE R__ARC4_BSDLIB)
if(DEFINED LIBBSDROOT)
target_include_directories(Core PRIVATE ${LIBBSDROOT}/include)
target_link_libraries(Core PRIVATE ${LIBBSDROOT}/lib/libbsd.so)
endif()
else()
CHECK_CXX_SOURCE_COMPILES("#include <sys/random.h>
int main() { char buf[32]; int res = getrandom(buf, 32, GRND_NONBLOCK); return 0;}" found_getrandom)
if(found_getrandom)
message(STATUS "Found getrandom in sys/random.h")
target_compile_definitions(Core PRIVATE R__GETRANDOM_CLIB)
else()
CHECK_CXX_SOURCE_RUNS("
#include <fstream>

int main() {
std::ifstream urandom{\"/dev/urandom\"};
if (!urandom) {
// This will make the CMake command fail
return 1;
}

constexpr int len{32};
char buf[len];
for (int n = 0; n < len; n++) buf[n] = 0;
urandom.read(buf, len);

int nmatch = 0;
for (int n = 0; n < len; n++)
if (buf[n] == 0) nmatch++;

// Fail if no values have changed
return nmatch != len ? 0 : 1;
}" found_urandom)
if(found_urandom)
message(STATUS "Found random device in /dev/urandom")
target_compile_definitions(Core PRIVATE R__USE_URANDOM)
else()
message(FATAL_ERROR "Fail to detect cryptographic random generator")
endif()
endif()
endif()
endif()
endif(UNIX)
26 changes: 26 additions & 0 deletions core/base/inc/ROOT/RCryptoRandom.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/// \file ROOT/RCryptoRandom.hxx
/// \ingroup Base
/// \date 2026-04-24

/*************************************************************************
* Copyright (C) 1995-2026, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/

#ifndef ROOT_RCryptoRandom
#define ROOT_RCryptoRandom

namespace ROOT {
namespace Internal {

/// Get random bytes from the operating system's cryptographic random number generator
/// The requested number of bytes must not exceed 256.
bool GetCryptoRandom(void *buf, unsigned int len);

} // namespace Internal
} // namespace ROOT

#endif
2 changes: 1 addition & 1 deletion core/base/inc/TDirectory.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ can be replaced with the simpler and exception safe:

TObject *fMother{nullptr}; ///< pointer to mother of the directory
TList *fList{nullptr}; ///< List of objects in memory
TUUID fUUID; ///< Unique identifier
TUUID fUUID{TUUID::UUIDv4()}; ///< Unique identifier
mutable TString fPathBuffer; ///<! Buffer for GetPath() function
TContext *fContext{nullptr}; ///<! Pointer to a list of TContext object pointing to this TDirectory

Expand Down
2 changes: 1 addition & 1 deletion core/base/inc/TSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ class TSystem : public TNamed {
void SetErrorStr(const char *errstr);
const char *GetErrorStr() const { return GetLastErrorString(); }
virtual const char *GetError();
virtual Int_t GetCryptoRandom(void *buf, Int_t len);
static Int_t GetCryptoRandom(void *buf, Int_t len);
void RemoveOnExit(TObject *obj);
virtual const char *HostName();
virtual void NotifyApplicationCreated();
Expand Down
9 changes: 9 additions & 0 deletions core/base/inc/TUUID.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,16 @@ class TUUID {
void GetRandomInfo(UChar_t seed[16]);
void SetFromString(const char *uuid_str);

struct TV4Marker {};
explicit TUUID(TV4Marker);

public:
TUUID();
TUUID(const char *uuid_str);
virtual ~TUUID();

static TUUID UUIDv4();

const char *AsString() const;
Int_t Compare(const TUUID &u) const;
UShort_t Hash() const;
Expand Down Expand Up @@ -100,5 +105,9 @@ inline Bool_t operator==(const TUUID &u1, const TUUID &u2)
inline Bool_t operator!=(const TUUID &u1, const TUUID &u2)
{ return !(u1 == u2); }

inline Bool_t operator<(const TUUID &u1, const TUUID &u2)
{
return u1.Compare(u2) == -1;
}

#endif
47 changes: 47 additions & 0 deletions core/base/src/RCryptoRandom.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "ROOT/RConfig.hxx"
#include "ROOT/RCryptoRandom.hxx"

#ifdef WIN32
#include "Windows4Root.h"
#include <bcrypt.h>
#else
#if defined(R__ARC4_STDLIB)
#include <cstdlib>
#elif defined(R__ARC4_BSDLIB)
#include <bsd/stdlib.h>
#elif defined(R__GETRANDOM_CLIB)
#include <sys/random.h>
#elif defined(R__USE_URANDOM)
#include <fstream>
#endif
#endif

bool ROOT::Internal::GetCryptoRandom(void *buf, unsigned int len)
{
if (len > 256)
return false;

#ifdef WIN32

return BCryptGenRandom((BCRYPT_ALG_HANDLE)NULL, (PUCHAR)buf, (ULONG)len, BCRYPT_USE_SYSTEM_PREFERRED_RNG) == 0;

#else // UNIX

#if defined(R__ARC4_STDLIB) || defined(R__ARC4_BSDLIB)
arc4random_buf(buf, len);
return true;
#elif defined(R__GETRANDOM_CLIB)
return getrandom(buf, len, GRND_NONBLOCK) == len;
#elif defined(R__USE_URANDOM)
std::ifstream urandom{"/dev/urandom"};
if (!urandom)
return false;
urandom.read(reinterpret_cast<char *>(buf), len);
return urandom.good();
#else
Comment thread
vepadulano marked this conversation as resolved.
#error "Reliable cryptographic random function not defined"
return false;
#endif

#endif
}
12 changes: 8 additions & 4 deletions core/base/src/TSystem.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ allows a simple partial implementation for new OS'es.
*/

#include <ROOT/FoundationUtils.hxx>
#include <ROOT/RCryptoRandom.hxx>
#include "strlcpy.h"
#include "TSystem.h"
#include "TApplication.h"
Expand Down Expand Up @@ -258,13 +259,16 @@ const char *TSystem::GetError()

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

Int_t TSystem::GetCryptoRandom(void * /* buf */, Int_t /* len */)
Int_t TSystem::GetCryptoRandom(void *buf, Int_t len)
{
Error("GetCryptoRandom", "Not implemented");
return -1;
if (len < 0) {
return -1;
}
const auto rv = ROOT::Internal::GetCryptoRandom(buf, len);
return rv ? len : -1;
}


Expand Down
34 changes: 33 additions & 1 deletion core/base/src/TUUID.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ system clock tick, the UUID generator will stall until the
system clock catches up.
*/

#include <ROOT/RCryptoRandom.hxx>

#include "TROOT.h"
#include "TDatime.h"
#include "TUUID.h"
Expand All @@ -124,6 +126,7 @@ system clock catches up.
#include "Bytes.h"
#include "TVirtualMutex.h"
#include "ThreadLocalStorage.h"
#include <cassert>
#include <cstring>
#include <cstdlib>
#ifdef R__WIN32
Expand All @@ -145,6 +148,35 @@ system clock catches up.
#endif
#include <chrono>

////////////////////////////////////////////////////////////////////////////////
/// Create a UUID version 4 (variant 1) UUID according to RFC 4122.
/// The UUIDv4 also has 16 octets but all but the version and variant information is random.
/// This leaves 122 random bits, which are filled by the system's cryptographic random number generator.
/// For all intents and purposes, the resulting UUIDs are actually globally unique.

TUUID TUUID::UUIDv4()
{
return TUUID{TV4Marker()};
}

////////////////////////////////////////////////////////////////////////////////
/// Create a version 4 UUID.

TUUID::TUUID(TV4Marker)
{
// Ensure we can treat the memory starting at uuid.fTimeLow as an array of 16 octets
assert(&fNode[5] - reinterpret_cast<unsigned char *>(&fTimeLow) + 1 == 16);

const auto rv = ROOT::Internal::GetCryptoRandom(&fTimeLow, 16);
R__ASSERT(rv);
// Fix up variant
fClockSeqHiAndReserved = (fClockSeqHiAndReserved & 0x3F) | (2 << 6);
// Fix up version
fTimeHiAndVersion = (fTimeHiAndVersion & 0x0FFF) | (4 << 12);

// TODO(jblomer): we do what the default constructor does but is this still used? Can it be deprecated?
fUUIDIndex = 1 << 30;
}

////////////////////////////////////////////////////////////////////////////////
/// Create a UUID.
Expand Down Expand Up @@ -569,7 +601,7 @@ void TUUID::Print() const

const char *TUUID::AsString() const
{
static char uuid[40];
TTHREAD_TLS(char) uuid[40];
Comment thread
silverweed marked this conversation as resolved.

snprintf(uuid,40, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
fTimeLow, fTimeMid, fTimeHiAndVersion, fClockSeqHiAndReserved,
Expand Down
4 changes: 4 additions & 0 deletions core/base/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ ROOT_ADD_GTEST(CoreErrorTests TErrorTests.cxx LIBRARIES Core)

ROOT_ADD_GTEST(CoreSystemTests TSystemTests.cxx LIBRARIES Core)

ROOT_ADD_GTEST(CoreCryptoRandomTest CryptoRandomTest.cxx LIBRARIES Core)

ROOT_ADD_GTEST(CoreColorTests TColorTests.cxx LIBRARIES Core)
if(CMAKE_SYSTEM_NAME MATCHES "Darwin" AND CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 25.4)
set_tests_properties(gtest-core-base-CoreColorTests PROPERTIES DISABLED True)
Expand All @@ -49,3 +51,5 @@ endif()

configure_file(Foo.C Foo.C COPYONLY)
ROOT_ADD_GTEST(IncludePathTest IncludePathTest.cxx LIBRARIES Core)

ROOT_ADD_GTEST(UUIDTest UUIDTest.cxx LIBRARIES Core)
39 changes: 39 additions & 0 deletions core/base/test/CryptoRandomTest.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "gtest/gtest.h"

#include <ROOT/RCryptoRandom.hxx>

TEST(TSystem, CryptoRandom)
{
// test with 512 bits, longer keys may not work

const int len = 64;
uint8_t buf[64];

for (int n = 0; n < len; n++)
buf[n] = 0;

EXPECT_TRUE(ROOT::Internal::GetCryptoRandom(buf, len));

int nmatch = 0;

for (int n = 0; n < len; n++)
if (buf[n] == 0)
nmatch++;

// check that values in buffer changed
EXPECT_TRUE(nmatch != len);

for (int n = 0; n < len; n++)
buf[n] = n;

EXPECT_TRUE(ROOT::Internal::GetCryptoRandom(buf, len));

nmatch = 0;

for (int n = 0; n < len; n++)
if (buf[n] == n)
nmatch++;

// check that values in buffer changed
EXPECT_TRUE(nmatch != len);
}
40 changes: 0 additions & 40 deletions core/base/test/TSystemTests.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -56,43 +56,3 @@ TEST(TSystem, TempFileSuffix)

gSystem->Unlink(fname);
}

TEST(TSystem, CryptoRandom)
{
// test with 512 bits, longer keys may not work

const int len = 64;
uint8_t buf[64];

for (int n = 0; n < len; n++)
buf[n] = 0;

auto res = gSystem->GetCryptoRandom(buf, len);

EXPECT_EQ(res, len);

int nmatch = 0;

for (int n = 0; n < len; n++)
if (buf[n] == 0)
nmatch++;

// check that values in buffer changed
EXPECT_TRUE(nmatch != len);

for (int n = 0; n < len; n++)
buf[n] = n;

res = gSystem->GetCryptoRandom(buf, len);

EXPECT_EQ(res, len);

nmatch = 0;

for (int n = 0; n < len; n++)
if (buf[n] == n)
nmatch++;

// check that values in buffer changed
EXPECT_TRUE(nmatch != len);
}
Loading
Loading