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.
- Multiple Entropy Sources: Native support for RANDOM.ORG, ANU Quantum Random Numbers, and the Drand Distributed Randomness Beacon.
- Unbiased Range Generation: Includes an
EntropyBufferthat 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
uint8output limits on ANU).
Install via Composer:
composer require michaelfrank-dev/php-trngPSR-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 usingunpack('J', ...). On a 32-bit PHP build, values exceedingPHP_INT_MAXare silently returned as floats, breaking type safety. This library requires a 64-bit PHP build.
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.
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, ...]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 useAnuProvider::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
uint8values, the library automatically requestsuint16from the API and unpacks them internally, yielding up to 2,048uint8values 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 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();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:
EntropyBufferoperates on bytes (uint8, values 0-255). If you fetcheduint16values from a provider, you must convert them first usingEntropyBuffer::fromUint16().
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();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
uint16values directly tonew EntropyBuffer()will throw an\InvalidArgumentException, as the constructor enforces that all values are valid bytes (0-255).
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);
}
}This library is open-sourced software licensed under the MIT license.