Ctrng-lib

Ctrng-lib

In our previous post, we outlined the randomness supply chain and how we can ensure randomness is correctly generated and delivered to the user. But once the user has that randomness, along with proof that it hasn't been tampered with, how does he use it? That’s the role of our crypto-ctrng lib: give users the tools to easily integrate that randomness into an application.

Whether it is a cryptographic application like a key generation, an NFT mint, a lottery, or even a game, everyone needs secure randomness: we don’t want someone to guess our key, to mint the best NFT intentionally, or to loot the best items in a game. That’s what we plan to provide at SpaceComputer: safe randomness, from the generation, through its transportability, to its final usability.

SpaceComputer generates randomness in space by running TEEs on orbital hardware that measures space radiation to extract entropy. 

Users or applications on Earth will communicate via Orbitport. Orbitport acts as the gateway that transports this randomness from the orbital environment to users and applications on Earth. 

Orbitport maintains public randomness beacons published on IPFS that can be consumed independently by each user to obtain random blocks with some metadata. So why build a library around it? The randomness provided by our cTRNG is true randomness, precisely, 32-byte blocks. That’s all. Either you know very well what you are doing, and that’s enough for you, or you need some machinery on top of it: you need fallbacks for when that source is failing, you need a Cryptographically Secure Pseudo Random Number Generator (CSPRNG) to extend that source when you need more than 32-byte blocks…Let’s dive into our library and see what we propose. 

The library is built in three layers:

image of the three layers to the crypto-ctrng library process.

Layer 1 defines a single trait that all entropy sources implement. One method returns a 32-byte block, or an error. 

Layer 2 provides the actual entropy sources:

  • IpfsCtrng fetches blocks from the randomness beacon over IPFS. 
  • LocalRng wraps the os entropy via getrandom.
  • MixedCtrng combines any remote source with local OS entropy by XORing them together.

Layer 3 wraps sources for practical use

  • BlockRng buffers raw 32-byte blocks.
  • ReseedingRng wraps any source with a ChaCha20 DRBG that automatically reseeds based on a configurable threshold (every 3200 bytes or every week, by default). This is recommended from the NIST specs (as explained in the previous article).

Now, you can directly call a reseedingRng that either uses only cTRNG randomness or cTRNG that is XORed with local randomness, and have it automatically reseed a CSPRNG! Without having to worry about prediction attacks, timeouts, and fallbacks!

Library Goals

1. Uniqueness

If you're just fetching blocks, you have no protection against receiving the same block twice (whether due to caching, network issues, or a misbehaving gateway). The library tracks the last served block and enforces timestamp monotonicity: every block must have a timestamp strictly greater than the previous one. It also rejects blocks with duplicate data, catching cases where the same randomness appears under different timestamps.

2. Efficiency

ctrng-lib implements a buffer, so that you fetch entropy only when the buffer is empty, not at every call. 

3. Security

Mixed Ctrng ensures that an attacker would need to compromise both the space source and your local source in order to predict a random number.

Finally, the library implements RngCore and CryptoRng, so you can use it as an RNG and easily replace your previous source of randomness with this new one.

Application & Envisioned Use Cases

We can use this library wherever randomness is needed. Let’s start with a simple example: ECDSA key generation. Here, randomness is the only input. In the original implementation, it relies on the operating system's entropy source via OsRng:

use k256::ecdsa::SigningKey;
use rand::rngs::OsRng;

let signing_key = SigningKey::random(&mut OsRng);

Our updated version replaces OsRng with space-based entropy fetched from an IPFS beacon:

use k256::ecdsa::SigningKey;
use rand_chacha::ChaCha20Rng;
use rand_core::SeedableRng;
use crypto_ctrng::{Ctrng,RandomBlockSource};


let beacon_key = "k2k4r8lvomw737sajfnpav0dpeernugnryng50uheyk1k39lursmn09f";
let mut ctrng = Ctrng::ipfs(beacon_key, None);
let seed = ctrng.next_block().expect("failed to fetch IPFS block");
let mut rng = ChaCha20Rng::from_seed(seed);
let signing_key = SigningKey::random(&mut rng);

One RNG source replaces the OsRng source; the rest is unchanged.

A More Complex Example: Threshold Schemes

Now, let’s take a look at a more complex example: in threshold schemes, randomness is a critical part, and we demonstrate several examples of RNG replacements with the ctrng lib.

Here, in cggmp24 implementation from dfns, we've integrated space-based randomness into the full threshold signature pipeline. The integration demonstrates how to replace traditional RNG sources with entropy sourced from IPFS while maintaining security and determinism.

The cggmp24 protocol requires randomness at three distinct stages: key generation, auxiliary information generation, and signing. In the original implementation, each party used rng.fork() (which uses os randomness) to create independent random streams. Our updated pipeline replaces this with space-based entropy while preserving the security properties.

Step 1: Fetching Base Seeds

Before the protocol begins, each party fetches a unique 32-byte block from the IPFS beacon:

    let beacon_key = "k2k4r8lvomw737sajfnpav0dpeernugnryng50uheyk1k39lursmn09f";
    let mut ctrng = Ctrng::ipfs(beacon_key, None);
    let mut base_seeds: Vec<[u8; 32]> = Vec::new();
    for i in 0..n {
        let seed = ctrng.next_block().expect("failed to fetch IPFS block");
        base_seeds.push(seed);
    }

Step 2: Domain-Separated Seed Derivation

Rather than using the base seeds directly, we derive stage-specific seeds using derive_seed. This function uses SHA-256 with domain separation, combining:

  • The execution ID (unique per protocol run)
  • The party ID (which party this is)
  • A stage counter (1 for keygen, 2 for aux_gen, 3 for signing)
  • The base seed from IPFS
    let stage_seed = derive_seed(eid.as_bytes(), i, 1, &base_seeds[usize::from(i)]);
    let mut party_rng = ChaCha20Rng::from_seed(stage_seed);

This ensures that:

  • Each party has independent randomness (different base seeds)
  • Each protocol stage has independent randomness (different counters)
  • Each protocol execution has independent randomness (different execution IDs)
  • The randomness is deterministic and reproducible given the same inputs

Step 3: Protocol Execution

Each stage of the protocol (keygen, aux_gen, signing) uses its derived seed to initialize a ChaCha20Rng. The protocol then runs normally, with all randomness coming from this space-seeded generator:

    // Key generation (stage counter = 1)
    let stage_seed = crypto_ctrng::derive_seed(eid.as_bytes(), i, 1, &base_seeds[usize::from(i)]);
    let mut party_rng = ChaCha20Rng::from_seed(stage_seed);
    keygen.start(&mut party_rng, party).await
    // Auxiliary info generation (stage counter = 2)
    let stage_seed = crypto_ctrng::derive_seed(eid.as_bytes(), i, 2, &base_seeds[usize::from(i)]);
    let mut party_rng = ChaCha20Rng::from_seed(stage_seed);
    aux_info_gen(eid, i, n, pregenerated_data).start(&mut party_rng, party).await
    // Signing (stage counter = 3)
    let stage_seed = crypto_ctrng::derive_seed(eid.as_bytes(), pid, 3, &base_seeds[usize::from(pid)]);
    let mut party_rng = ChaCha20Rng::from_seed(stage_seed);
    signing.sign(&mut party_rng, party, message_to_sign).await

For now, only public beacons are available, but soon we plan to extend our cTRNG to support private beacons, so you can have randomness that is truly random, not tampered with, and also private! 

Stay tuned for more use cases in the following articles! 


Follow SpaceComputer on X (Twitter) and LinkedIn.
Head to Github for crypto-ctrng lib access.
Explore Orbitport and cTRNG Documentation.

Discover your next read:

Verifying True Randomness in Cryptographic Systems
Randomness is a foundational primitive in cryptographic protocols. It underpins security properties such as unpredictability, fairness, privacy, and resistance to adversarial manipulation. Key generation, nonces, salts, signatures, leader election, lotteries, and many consensus mechanisms all rely on randomness behaving as assumed. When randomness is weak, biased, predictable, or manipulable, entire