Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/octra-labs/pvac_hfhe_cpp/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This module provides cryptographically secure random number generation (CSPRNG) using platform-specific secure random sources. All randomness is suitable for cryptographic key generation and security-critical operations.

Core functions

csprng_bytes

Generates cryptographically secure random bytes.
void csprng_bytes(uint8_t* out, size_t n);
out
uint8_t*
Output buffer to fill with random bytes
n
size_t
Number of random bytes to generate
Example:
uint8_t key[32];
csprng_bytes(key, 32); // Generate 256-bit random key
This function calls std::abort() if the system random source fails. This is intentional to prevent insecure fallback behavior.

csprng_u64

Generates a cryptographically secure random 64-bit unsigned integer.
uint64_t csprng_u64();
return
uint64_t
Random 64-bit value
Example:
uint64_t random_tag = csprng_u64();
uint64_t random_seed = csprng_u64();

Utility functions

load_le64

Loads a 64-bit integer from a byte array in little-endian format.
uint64_t load_le64(const uint8_t* p);
p
const uint8_t*
Pointer to 8 bytes
return
uint64_t
64-bit integer in host byte order
Example:
uint8_t bytes[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
uint64_t value = load_le64(bytes);
// value = 0x0807060504030201

store_le64

Stores a 64-bit integer into a byte array in little-endian format.
void store_le64(uint8_t* p, uint64_t x);
p
uint8_t*
Output buffer (must have space for 8 bytes)
x
uint64_t
Value to store
Example:
uint8_t bytes[8];
store_le64(bytes, 0x0807060504030201ULL);
// bytes = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}

Platform-specific implementations

macOS / BSD

Uses arc4random_buf() for cryptographically secure random bytes.
arc4random_buf(out, n);
Available on macOS, FreeBSD, OpenBSD, and NetBSD.

Linux

Uses the getrandom() system call with fallback to /dev/urandom.
getrandom(out, n, 0);
  • Primary: getrandom() system call (Linux 3.17+)
  • Fallback: Reads from /dev/urandom if getrandom() fails
  • Handles interruptions (EINTR) automatically

Windows

Uses BCryptGenRandom() with the system-preferred RNG.
BCryptGenRandom(NULL, out, n, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
Requires bcrypt.lib (automatically linked via pragma).

Fallback (portable)

Uses std::random_device for platforms without native secure random support.
std::random_device rd;
The fallback implementation may not be cryptographically secure on all platforms. Prefer platforms with native secure random support for production use.

Security properties

Cryptographic strength

All platform-specific implementations provide:
  • Unpredictability: Output cannot be predicted from previous values
  • Uniform distribution: All bit patterns equally likely
  • Sufficient entropy: Backed by hardware or OS entropy sources
  • Forward secrecy: Compromise of current state doesn’t reveal past outputs

Error handling

If random generation fails, the library calls std::abort() rather than returning an error. This is a deliberate security decision:
  • Prevents accidental use of non-random or predictable values
  • Makes failures immediately visible during testing
  • Avoids complex error propagation through cryptographic code

Usage patterns

Key generation

// Generate 256-bit PRF key
std::array<uint64_t, 4> prf_key;
for (int i = 0; i < 4; i++) {
    prf_key[i] = csprng_u64();
}

Nonce generation

Nonce128 generate_nonce() {
    return Nonce128{
        .lo = csprng_u64(),
        .hi = csprng_u64()
    };
}

Random seed buffer

std::vector<uint64_t> generate_seed(size_t words) {
    std::vector<uint64_t> seed(words);
    csprng_bytes(reinterpret_cast<uint8_t*>(seed.data()),
                 words * sizeof(uint64_t));
    return seed;
}

Random field element

Fp random_field_element() {
    uint64_t lo = csprng_u64();
    uint64_t hi = csprng_u64() & MASK63; // Top bit must be 0
    return fp_from_words(lo, hi);
}

Testing considerations

For deterministic testing:
  • The CSPRNG is not seedable by design (security requirement)
  • For reproducible tests, use a separate PRNG (like SHAKE256)
  • Never use test-only random sources in production code
Example deterministic testing pattern:
#ifdef TESTING
  // Use deterministic PRNG for testing
  std::mt19937_64 test_rng(fixed_seed);
  return test_rng();
#else
  // Use secure random in production
  return csprng_u64();
#endif

Relationship to other modules

The random module is used by:
  • Types (types.hpp): make_nonce128(), rand_fp_nonzero()
  • Hash (hash.hpp): Seeding XOFs and PRNGs
  • Key generation: Generating secret keys and randomness
  • Encryption: Sampling error vectors and random masks

Performance characteristics

  • csprng_u64(): ~10-50 CPU cycles on modern hardware
  • csprng_bytes(): ~1-5 GB/s throughput for bulk generation
  • Dominated by system call overhead for small requests
  • Consider batching requests for small values
Batching example:
// Inefficient: many small calls
for (int i = 0; i < 1000; i++) {
    uint64_t x = csprng_u64();
    process(x);
}

// Efficient: batch generation
std::vector<uint64_t> randoms(1000);
csprng_bytes(reinterpret_cast<uint8_t*>(randoms.data()),
             1000 * sizeof(uint64_t));
for (uint64_t x : randoms) {
    process(x);
}
  • Types - Uses random generation for nonces and field elements
  • Hash - Deterministic randomness expansion via XOF
  • Field operations - Random field element generation

Build docs developers (and LLMs) love