diff --git a/.github/workflows/rust-vuln-scan.yml b/.github/workflows/rust-vuln-scan.yml new file mode 100644 index 0000000..3a643a5 --- /dev/null +++ b/.github/workflows/rust-vuln-scan.yml @@ -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 \ No newline at end of file diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..ecc560b --- /dev/null +++ b/CONTEXT.md @@ -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` | `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) +``` diff --git a/Cargo.lock b/Cargo.lock index 926a003..1beb529 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,9 +69,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" @@ -730,9 +730,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.13" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ "bytes", "getrandom 0.3.4", @@ -970,9 +970,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ "ring", "rustls-pki-types", diff --git a/README.md b/README.md index 7c86ae4..e0738bc 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 diff --git a/overview.md b/overview.md new file mode 100644 index 0000000..0412666 --- /dev/null +++ b/overview.md @@ -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. diff --git a/tests/testu01_tests.rs b/tests/testu01_tests.rs index 8d1caa8..3c52003 100644 --- a/tests/testu01_tests.rs +++ b/tests/testu01_tests.rs @@ -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");