Robust PHP library for retrieving true random numbers (TRNG) from quantum and hardware sources, featuring rejection sampling and resilient fallback chains.

Maintainers

Package info

github.com/michaelfrank-dev/php-trng

pkg:composer/michaelfrank-dev/php-trng

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-05-04 10:32 UTC

This package is auto-updated.

Last update: 2026-05-04 10:39:51 UTC


README

Minimum PHP Version Platform: 64-bit License: MIT

PHP library for retrieving True Random Numbers (TRNG) from multiple external hardware and quantum entropy sources.

Whether you need true, non-deterministic random numbers for cryptographic seeding, randomized trials, simulations, or procedural generation, this library provides a clean interface to fetch uint8 or uint16 integers natively. It features granular exception handling to allow resilient fallback chains.

Features

  • Multiple Entropy Sources: Native support for RANDOM.ORG, ANU Quantum Random Numbers, and the Drand Distributed Randomness Beacon.
  • Unbiased Range Generation: Includes an EntropyBuffer that derives specific types, bounds, and ranges safely without modular bias (rejection sampling).
  • Flexible Authentication: Most providers can be configured with or without API keys, adapting to your usage volume and tier.
  • Smart Limitations Bypass: Gracefully unpacks values behind the scenes when service quotas restrict payload limits (e.g., doubling uint8 output limits on ANU).

Installation

Install via Composer:

composer require michaelfrank-dev/php-trng

PSR-18 Requirement: This package requires a PSR-18 HTTP Client and PSR-17 HTTP Factories. If you don't already have one in your project, install Guzzle:

composer require guzzlehttp/guzzle

64-bit PHP Required: EntropyBuffer::toInteger(8) reads a 64-bit unsigned integer using unpack('J', ...). On a 32-bit PHP build, values exceeding PHP_INT_MAX are silently returned as floats, breaking type safety. This library requires a 64-bit PHP build.

Fetching Raw Random Numbers

The simplest use case is fetching raw random integers directly from a provider. All providers implement a getValues(int $length, TrngType $type) method that returns a plain PHP array.

RANDOM.ORG

Can be used with or without an API key.

  • Without an API Key — uses the legacy plain-text API.
  • With an API Key — upgrades to the JSON-RPC v4 API for higher limits and quota telemetry.

Terms of Use: The RANDOM.ORG open API is subject to fair-use rate limits and other restrictions. Review the usage guidelines before integrating it into production applications.

use MichaelFrank\Trng\Providers\RandomOrgProvider;
use MichaelFrank\Trng\TrngType;

// Without API key (legacy plain-text API)
$provider = new RandomOrgProvider();
$bytes = $provider->getValues(100, TrngType::UINT8);
// $bytes => [142, 7, 203, 88, ...]  (100 integers, each 0-255)

// With API key (JSON-RPC v4 API)
$provider = RandomOrgProvider::withApiKey('your-api-key-here');
$integers = $provider->getValues(50, TrngType::UINT16);
// $integers => [41230, 8912, 60001, ...]  (50 integers, each 0-65535)

// Quota metadata is available after any authenticated call
$metadata = $provider->getLastMetadata();
// e.g., ['requestsLeft' => 999, 'bitsLeft' => 249000, ...]

ANU Quantum Random Numbers (QRNG)

Can be used with or without an API key.

Deprecation Notice: The unauthenticated ANU endpoint (qrng.anu.edu.au) is being phased out and may be unavailable in the future. It is strongly recommended to register for an API key and use AnuProvider::withApiKey() for any production usage.

Note on API Limits: The ANU API enforces a strict limit of 1,024 items per request. When you request more than 1,024 uint8 values, the library automatically requests uint16 from the API and unpacks them internally, yielding up to 2,048 uint8 values transparently.

use MichaelFrank\Trng\Providers\AnuProvider;
use MichaelFrank\Trng\TrngType;

// Without API key
$provider = new AnuProvider();
$bytes = $provider->getValues(100, TrngType::UINT8);
// $bytes => [57, 200, 14, 99, ...]  (100 integers, each 0-255)

// Requesting more than 1024 uint8 — the library handles the unpacking internally
$bytes = $provider->getValues(2000, TrngType::UINT8);
// $bytes => [...]  (2000 integers, each 0-255)

// Requesting uint16 directly (max 1024)
$integers = $provider->getValues(100, TrngType::UINT16);
// $integers => [41230, 8912, 60001, ...]  (100 integers, each 0-65535)

// With API key
$provider = AnuProvider::withApiKey('your-api-key-here');
$bytes = $provider->getValues(512, TrngType::UINT8);

Drand (Distributed Randomness Beacon)

Drand yields exactly 32 bytes (or 16 uint16 values) per beacon call. No API key is required. The beacon signature is verified automatically to ensure cryptographic integrity. Note: It does not verify the drand BLS signature against the chain public key.

This provider targets the Quicknet chain by default (QUICKNET_CHAIN_HASH), which operates on a 3-second pulse interval with unchained, threshold BLS randomness.

use MichaelFrank\Trng\Providers\DrandProvider;
use MichaelFrank\Trng\TrngType;

$provider = new DrandProvider();

// Fetch up to 32 bytes
$bytes = $provider->getValues(32, TrngType::UINT8);
// $bytes => [...]  (32 integers, each 0-255)

// Fetch up to 16 uint16 values
$integers = $provider->getValues(16, TrngType::UINT16);
// $integers => [...]  (16 integers, each 0-65535)

// Use a custom chain or relay
$provider = DrandProvider::forChain('your-chain-hash', ['https://your.relay.example.com']);

// Disable beacon signature verification (not recommended for production)
$provider = (new DrandProvider())->withoutVerification();

Consuming Entropy Safely with EntropyBuffer

Raw random integers are useful on their own, but using modulo (%) to fit them into a range introduces modular bias, which skews the distribution. The EntropyBuffer class solves this by using rejection sampling to guarantee a perfectly uniform distribution.

Important: EntropyBuffer operates on bytes (uint8, values 0-255). If you fetched uint16 values from a provider, you must convert them first using EntropyBuffer::fromUint16().

Example: From uint8 (direct)

use MichaelFrank\Trng\Providers\AnuProvider;
use MichaelFrank\Trng\EntropyBuffer;
use MichaelFrank\Trng\TrngType;

$provider = new AnuProvider();
$bytes = $provider->getValues(64, TrngType::UINT8);

$buffer = new EntropyBuffer($bytes);

// Unbiased integer in a precise range (e.g., roll a six-sided die)
$diceRoll = $buffer->toRange(1, 6);

// Uniform float in [0.0, 1.0)
$float = $buffer->toFloat();

// Consume the next N bytes from the buffer (cursor advances)
$chunk = $buffer->nextBytes(4);

// Export the full buffer as hex or a binary string
$hex = $buffer->toHex();
$binary = $buffer->toBinaryString();

// Check how many bytes are still available
$remaining = $buffer->remaining();

Example: From uint16 (use the helper)

When you fetch uint16 values from a provider, pass them through EntropyBuffer::fromUint16(). It splits each 16-bit integer into two bytes (Big Endian) and returns a ready-to-use buffer with double the entropy.

use MichaelFrank\Trng\Providers\AnuProvider;
use MichaelFrank\Trng\EntropyBuffer;
use MichaelFrank\Trng\TrngType;

$provider = new AnuProvider();
$uint16s = $provider->getValues(16, TrngType::UINT16);
// $uint16s => [41230, 8912, ...]  (16 integers, each 0-65535)

// Convert to a byte buffer — each uint16 becomes 2 bytes (32 bytes total)
$buffer = EntropyBuffer::fromUint16($uint16s);

$diceRoll = $buffer->toRange(1, 6);
$float = $buffer->toFloat();

Passing uint16 values directly to new EntropyBuffer() will throw an \InvalidArgumentException, as the constructor enforces that all values are valid bytes (0-255).

Exception Handling & Fallback Chains

When working with external HTTP APIs, resilient error handling is critical. The library defines granular exceptions that all extend the base TrngException class.

Exception When it is thrown
TrngRateLimitException HTTP 429 or quota exhaustion
TrngNetworkException DNS failure, timeout, or unreachable host
TrngServerException HTTP 5xx server errors
TrngInvalidResponseException Malformed JSON, missing fields, or failed beacon integrity check
TrngException Base class; catches all of the above

The example below demonstrates a complete three-tier fallback chain across all providers:

use MichaelFrank\Trng\Providers\RandomOrgProvider;
use MichaelFrank\Trng\Providers\AnuProvider;
use MichaelFrank\Trng\Providers\DrandProvider;
use MichaelFrank\Trng\TrngType;

use MichaelFrank\Trng\Exceptions\TrngException;
use MichaelFrank\Trng\Exceptions\TrngNetworkException;
use MichaelFrank\Trng\Exceptions\TrngRateLimitException;
use MichaelFrank\Trng\Exceptions\TrngServerException;
use MichaelFrank\Trng\Exceptions\TrngInvalidResponseException;

$length = 256;
$type   = TrngType::UINT8;
$result = [];

try {
    // Primary: RANDOM.ORG (authenticated)
    $provider = RandomOrgProvider::withApiKey('your-api-key-here');
    $result = $provider->getValues($length, $type);

} catch (TrngRateLimitException $e) {
    // HTTP 429 or quota exhausted
    error_log('RANDOM.ORG quota depleted: ' . $e->getMessage());
    $result = fallbackToQuantum($length, $type);

} catch (TrngNetworkException $e) {
    // DNS failure, timeout, or host unreachable
    error_log('Network failure: ' . $e->getMessage());
    $result = fallbackToQuantum($length, $type);

} catch (TrngServerException $e) {
    // HTTP 5xx
    error_log('Provider server error: ' . $e->getMessage());
    $result = fallbackToQuantum($length, $type);

} catch (TrngInvalidResponseException $e) {
    // Malformed payload or failed integrity check
    error_log('Invalid response: ' . $e->getMessage());
    $result = fallbackToQuantum($length, $type);

} catch (TrngException $e) {
    // Catch-all for any other library exception
    error_log('Unexpected TRNG error: ' . $e->getMessage());
    $result = fallbackToQuantum($length, $type);

} catch (\InvalidArgumentException $e) {
    // Invalid $length or $type parameters
    error_log('Invalid arguments: ' . $e->getMessage());
}

function fallbackToQuantum(int $length, TrngType $type): array
{
    try {
        // Secondary: ANU Quantum (no key needed)
        return (new AnuProvider())->getValues($length, $type);

    } catch (TrngException $e) {
        // Last resort: Drand beacon (max 32 bytes per call)
        return (new DrandProvider())->getValues(min($length, 32), $type);
    }
}

License

This library is open-sourced software licensed under the MIT license.