Skip to content
26 changes: 26 additions & 0 deletions .github/workflows/rust-vuln-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Rust Vulnerability Scan

on:
push:
branches: [ "main" ]
pull_request:
paths:
- '**/Cargo.toml'
- '**/Cargo.lock'
# schedule:
# # Run daily at midnight to catch newly disclosed vulnerabilities
# - cron: '0 0 * * *'

jobs:
audit:
name: Run cargo-audit
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install cargo-audit
uses: taiki-e/install-action@cargo-audit

- name: Run cargo-audit
run: cargo audit
88 changes: 88 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Project context

> For AI assistants.

## Purpose

**crypto-ctrng** is a Rust crate that provides secure randomness sourced from a cosmic True Random Number Generator (cTRNG) backend. It wraps this entropy into the standard `rand_core::RngCore` / `CryptoRng` traits so any Rust cryptographic library can consume it directly.

The crate does **not** generate randomness itself; it **fetches, validates, and delivers** 32-byte blocks from one or more remote backends, enforcing timestamp monotonicity and duplicate detection.

## Architecture

```
User code
│
▼
Ctrng ← public enum; one variant per backend (currently Ipfs)
│
├─► IpfsCtrng ← fetches JSON beacon via IPFS gateways, caches blocks
│
├─► (future: DataHeavenCtrng, hardware TRNG, …)
│
▼
RandomBlockSource trait ← fn next_block() -> Result<[u8; 32], SourceError>
│
▼
MixedCtrng ← XORs remote block with OS entropy (defense in depth)
│
▼
ReseedingRng ← ChaCha20-based DRBG, auto-reseeds from source
│
▼
RngCore / CryptoRng ← standard rand_core traits
```

## Key types

| Type | File | Role |
|------|------|------|
| `RandomBlockSource` | `src/traits.rs` | Trait every entropy backend must implement |
| `Ctrng` | `src/ctrng/mod.rs` | Public enum dispatching to concrete backends |
| `IpfsCtrng` | `src/ctrng/ipfs.rs` | IPFS beacon client (internal, behind `Ctrng`) |
| `IpfsConfig` | `src/ctrng/ipfs.rs` | Gateway list, `use_defaults`, timeout |
| `IpfsGateway` | `src/ctrng/ipfs.rs` | Newtype for gateway URL; builds `/ipfs/` and `/ipns/` paths |
| `CtrngBlock` | `src/ctrng/types.rs` | Block with sequence number, timestamp, 32-byte data |
| `MixedCtrng<R>` | `src/mixed.rs` | XOR of remote + local OS entropy |
| `ReseedingRng` | `src/reseed.rs` | ChaCha20 DRBG with configurable reseed intervals |
| `BlockRng` | `src/rng.rs` | Thin `RngCore` adapter over any `RandomBlockSource` |
| `LocalRng` | `src/local.rs` | OS-level randomness (`getrandom`) |
| `SourceError` | `src/error.rs` | Error enum: `Ctrng(String)` / `Os(String)` |

## File layout

```
src/
├── lib.rs public re-exports
├── traits.rs RandomBlockSource trait
├── error.rs SourceError
├── ctrng/
│ ├── mod.rs Ctrng enum + RandomBlockSource impl
│ ├── ipfs.rs IpfsCtrng, IpfsGateway, IpfsConfig, DEFAULT_GATEWAYS
│ └── types.rs BeaconResponse, CtrngBlock
├── mixed.rs MixedCtrng (XOR mixer)
├── reseed.rs ReseedingRng, ReseedConfig
├── rng.rs BlockRng (RngCore adapter)
└── local.rs LocalRng (OS entropy)

tests/
├── ipfs_live.rs live IPFS gateway tests (#[ignore])
├── mixed_tests.rs MixedCtrng tests (some #[ignore])
├── reseed_tests.rs ReseedingRng offline tests
├── rng_tests.rs BlockRng error propagation
└── statistical_tests.rs TestU01 / PractRand (behind features)
```

## Conventions

- **PRs**: follow [.github/pull_request_template.md](.github/pull_request_template.md) — compiles without warnings, `cargo test` passes, clear commits, link issue if applicable.
- **Tests**: `cargo test` runs offline tests only. Online (IPFS) tests are `#[ignore]` — run with `cargo test -- --include-ignored`.

## Useful commands

```bash
cargo test # offline unit + integration
cargo test -- --include-ignored # includes live IPFS tests
cargo test --features testu01 # TestU01 SmallCrush
cargo test --features practrand # PractRand (1 MiB)
```
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
**WARNING: This project is experimental and work-in-progress, use at your own risk!**
# SpaceComputer | crypto-ctrng

![spacecomputer logo](https://raw.githubusercontent.com/spacecomputer-io/media-kit/refs/heads/main/SpaceComputer/logo/SpaceComputer_banner.png)

![Tests](https://github.com/spacecomputer-io/crypto-ctrng/actions/workflows/rust.yml/badge.svg?branch=main)

This repository contains the crypto-ctrng project by SpaceComputer.

## Overview

This crate provides a lightweight and secure abstraction layer over a cosmic True Random Number Generator (cTRNG) backend.
It allows deterministic or hardware-backed randomness to be consumed through the standard `RngCore` and `CryptoRng` traits from `rand_core`.

The main goals is to expose a clean, composable API for hardware or remote entropy backends. It can serve as the randomness foundation for distributed protocols such as threshold ECDSA, or MPC systems that require reproducible yet secure entropy sources.
The main goals is to expose a clean, composable API for hardware or remote entropy backends.

## Links

* [SpaceComputer docs](https://docs.spacecomputer.io)

### Entropy ownership contract

Expand Down Expand Up @@ -134,6 +144,13 @@ cargo test --test e2e_ipfs
cargo test --test e2e_mixed
```

You can also run the dedicated e2e integration targets individually:

```bash
cargo test --test e2e_ipfs
cargo test --test e2e_mixed
```

To run the IPFS-backed end-to-end tests :

```bash
Expand Down
146 changes: 146 additions & 0 deletions overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
**WARNING: This project is experimental and work-in-progress, use at your own risk!**

## Overview

This crate provides a lightweight and secure abstraction layer over a cosmic True Random Number Generator (cTRNG) backend.
It allows deterministic or hardware-backed randomness to be consumed through the standard `RngCore` and `CryptoRng` traits from `rand_core`.

The main goals is to expose a clean, composable API for hardware or remote entropy backends.

### Entropy ownership contract

This crate's responsibility is to **verify** that the cTRNG backend provides unique blocks. The randomness flow is split between three layers:

- **Gateway/back-end** (e.g. IPFS beacon, hardware TRNG) is responsible for generating unique 32-byte blocks with monotonic timestamps. This responsibility sits outside of this crate.
- **This crate** (`crypto-ctrng`) verifies uniqueness by enforcing timestamp monotonicity and rejecting duplicate blocks.
- **Application/client code** (e.g. TECDSA library) is responsible for personalizing the provided blocks to domain-separate pulls so a single user never receives the same derived value twice, even if they consume the same cTRNG block as other users (but this is something that we are looking to prevent in the future).

**Note on entropy mixing:**
- `MixedCtrng` combines entropy sources (XOR of IPFS beacon + local OS randomness) for enhanced security, but still relies on the gateway for uniqueness guarantees.

**Uniqueness guarantees:**
- This crate ensures that each `next_block()` call returns a block with a timestamp strictly greater than the previous one, preventing global block reuse.
- The calling library must personalize blocks per user/execution to prevent collisions between different users reading the same raw block.

## Usage modes

| Mode | Code | DRBG | Reseed |
|------|------|------|--------|
| Raw entropy | `BlockRng::new(source)` | N/A | N/A |
| Seeded DRBG (testing) | `ChaCha20Rng::from_seed(seed)` | ChaCha20 | N/A |
| DRBG + auto reseed | `ReseedingRng::new(source)` | ChaCha20 | 3200 bytes / 1 week |
| DRBG + prediction resistance | `ReseedingRng::with_prediction_resistance(source)` | ChaCha20 | every call |

## Usage

To use this crate, add it to your `Cargo.toml`:

```toml
[dependencies]
crypto-ctrng = { git = "https://github.com/spacecomputer-io/crypto-ctrng.git", tag = "v0.1.0" }
```

### Fetch raw entropy (default gateways)

```rust
use crypto_ctrng::{Ctrng, RandomBlockSource};

let beacon_key = "k2k4r8lvomw737sajfnpav0dpeernugnryng50uheyk1k39lursmn09f";

let mut ctrng = Ctrng::ipfs(beacon_key, None);
let block = ctrng.next_block().expect("failed to fetch IPFS block");
```

### Custom gateways with automatic fallback

If a gateway is unresponsive or down, the next one in the list is tried automatically.
The default timeout is 10 seconds per gateway.

```rust
use crypto_ctrng::{Ctrng, IpfsConfig, RandomBlockSource};
use std::time::Duration;

let beacon_key = "k2k4r8lvomw737sajfnpav0dpeernugnryng50uheyk1k39lursmn09f";

// Custom gateways + default gateways appended automatically
let config = IpfsConfig {
gateways: vec!["https://my-gateway.example.com".into()],
..Default::default()
};
let mut ctrng = Ctrng::ipfs(beacon_key, Some(config));
let block = ctrng.next_block().expect("at least one gateway should respond");

// Custom gateways only (no defaults), with a custom timeout
let config = IpfsConfig {
gateways: vec![
"https://ipfs.filebase.io".into(),
"https://ipfs.io".into(),
],
use_defaults: false,
timeout: Duration::from_secs(5),
};
let mut ctrng = Ctrng::ipfs(beacon_key, Some(config));
```

### DRBG with automatic reseeding (recommended)

```rust
use crypto_ctrng::{Ctrng, MixedCtrng, ReseedingRng};
use rand_core::RngCore;

let beacon_key = "k2k4r8lvomw737sajfnpav0dpeernugnryng50uheyk1k39lursmn09f";

let ipfs = Ctrng::ipfs(beacon_key, None);
let mixed = MixedCtrng::new(ipfs).unwrap();
let mut rng = ReseedingRng::new(mixed).unwrap();

let mut key = [0u8; 32];
rng.fill_bytes(&mut key);
```

### Custom reseed intervals

```rust
use std::time::Duration;
use crypto_ctrng::{Ctrng, MixedCtrng, ReseedConfig, ReseedingRng};

let beacon_key = "k2k4r8lvomw737sajfnpav0dpeernugnryng50uheyk1k39lursmn09f";

let ipfs = Ctrng::ipfs(beacon_key, None);
let mixed = MixedCtrng::new(ipfs).unwrap();

let config = ReseedConfig::new(1600, Duration::from_secs(3600)); // 50 blocks, 1 hour
let mut rng = ReseedingRng::with_config(mixed, config).unwrap();
```

## Tests

To run offline tests :

```cargo test```

To run offline and online tests :

```cargo test -- --include-ignored```

### Statistical verification

Statistical testing is provided by the separate [`rng-statistical-tests`](https://github.com/spacecomputer-io/statistical-verification) library, which supports both **TestU01** and **PractRand**.

TestU01 and PractRand are automatically downloaded and built during compilation - no manual setup required.

To run statistical tests:

```bash
cargo test --features testu01 # TestU01 SmallCrush
cargo test --features practrand # PractRand (1 MiB)
```


## License

This project is licensed under the terms of the MIT License. See the [LICENSE](LICENSE) file for details.

## Contributing

We welcome contributions to this project! If you have suggestions for improvements or new features, please open an issue or submit a pull request.
1 change: 0 additions & 1 deletion tests/testu01_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use rng_statistical_tests::testu01::{

#[cfg(feature = "testu01")]
fn build_ipfs_seeded_rng() -> ChaCha20Rng {
let gateway = "https://ipfs.filebase.io";
let beacon_key = "k2k4r8lvomw737sajfnpav0dpeernugnryng50uheyk1k39lursmn09f";
let mut ctrng = Ctrng::ipfs(beacon_key, None);
let seed = ctrng.next_block().expect("failed to fetch IPFS block");
Expand Down
Loading