Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,6 @@ Thumbs.db
.env.local
.env.*.local
secrets.json

# Demos
demo_*.py
78 changes: 77 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,80 @@ TrueEntropy harvests entropy from real-world sources and converts it into crypto

---

## Operation Modes

TrueEntropy supports two operation modes, selectable via `configure(mode=...)`:

### Mode Comparison

| Aspect | DIRECT Mode | HYBRID Mode |
|--------|-------------|-------------|
| **Speed** | ~60K ops/sec | ~5M ops/sec |
| **Source** | Direct pool extraction | PRNG seeded by pool |
| **Security** | Maximum (true random) | High (periodic reseed) |
| **Use Case** | Crypto keys, wallets | Simulations, games |

### DIRECT Mode (Default)

Every call extracts fresh entropy directly from the pool:

```
trueentropy.random() ──► EntropyTap.random() ──► pool.extract(8) ──► float
```

```
┌────────────────────────────────────────────────────────────────────┐
│ DIRECT MODE │
├────────────────────────────────────────────────────────────────────┤
│ │
│ random() ───► EntropyTap ───► Pool ───► 8 bytes ───► float │
│ │ │
│ ▲ │
│ Harvesters │
│ │
└────────────────────────────────────────────────────────────────────┘
```

### HYBRID Mode

Uses a fast PRNG (Mersenne Twister) that re-seeds from the pool periodically:

```
trueentropy.random() ──► HybridTap.random() ──► PRNG.random() ──► float
└──► (every N seconds) ──► pool.extract(32) ──► PRNG.seed()
```

```
┌────────────────────────────────────────────────────────────────────┐
│ HYBRID MODE │
├────────────────────────────────────────────────────────────────────┤
│ │
│ random() ───► HybridTap ───► PRNG (Mersenne Twister) ───► float │
│ │ │
│ ▼ (periodic reseed) │
│ Pool ◄────── Harvesters │
│ │ │
│ └──► 32 bytes ──► PRNG.seed() │
│ │
└────────────────────────────────────────────────────────────────────┘
```

### Configuration

```python
import trueentropy

# DIRECT mode (default) - maximum security
trueentropy.configure(mode="DIRECT")

# HYBRID mode - maximum performance
trueentropy.configure(mode="HYBRID", hybrid_reseed_interval=60.0)

# Combined: Hybrid + Offline (fast, no network)
trueentropy.configure(mode="HYBRID", offline_mode=True)
```

## Entropy Sources

### 1. Timing Jitter (timing.py)
Expand Down Expand Up @@ -300,7 +374,9 @@ Guarantees all N! permutations are equally probable.
| Module | Purpose |
|--------|---------|
| `pool.py` | Accumulates and mixes entropy with SHA-256 |
| `tap.py` | Extracts entropy and converts to types |
| `tap.py` | `BaseTap` abstract class + `EntropyTap` (DIRECT mode) |
| `hybrid.py` | `HybridTap` for HYBRID mode (PRNG seeded by pool) |
| `config.py` | `TrueEntropyConfig` dataclass + `configure()` function |
| `collector.py` | Background thread for automatic collection |
| `health.py` | Monitors pool health (score 0-100) |
| `harvesters/` | Collectors for different entropy sources |
Expand Down
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,43 @@ print_health_report(get_pool())
> **Note**: Offline mode provides reduced entropy diversity. For security-critical applications, consider using all available sources when network access is available.


## Direct vs Hybrid Mode

TrueEntropy offers two modes of operation to balance security and performance:

| Mode | Entropy Source | Performance | Use Case |
|------|---------------|-------------|----------|
| **DIRECT** (Default) | Directly from entropy pool | Slower, blocking | Cryptographic keys, wallets, severe security |
| **HYBRID** | PRNG seeded by pool | Extremely fast | Simulations, games, UI, general purpose |

### Using Hybrid Mode

Hybrid mode uses the TrueEntropy pool to periodically re-seed a fast pseudo-random number generator (PRNG). This provides the best of both worlds: the speed of standard Python random numbers with the entropy quality of our harvesters.

```python
import trueentropy

# Configure Hybrid Mode (re-seed every 60 seconds)
trueentropy.configure(mode="HYBRID", hybrid_reseed_interval=60.0)

# Generate numbers at max speed
# The internal PRNG is automatically re-seeded from the entropy pool
for _ in range(1000000):
val = trueentropy.random()
```

### Tuning Hybrid Mode

The `hybrid_reseed_interval` should be chosen based on your `offline_mode` setting:

* **Online (Default)**: Network harvesters take time to collect entropy (latency). Set the interval to **10.0s or higher** to allow the pool to refill between reseeds.
* **Offline (`offline_mode=True`)**: Local sources are near-instant. You can use lower intervals (e.g., **1.0s - 2.0s**) for frequent reseeding.

> **Tip**: If `health()` reports degraded status with low entropy bits, increase your reseed interval.




## Advanced Features

### Async Support
Expand Down Expand Up @@ -278,10 +315,14 @@ True random numbers from quantum phenomena:

| Function | Description |
|----------|-------------|
| `configure(...)` | Set mode (DIRECT/HYBRID), offline_mode, enable sources |
| `reset_config()` | Reset configuration to defaults |
| `health()` | Returns entropy pool health status |
| `start_collector(interval)` | Starts background entropy collection |
| `stop_collector()` | Stops background collection |
| `feed(data)` | Manually feed entropy into the pool |
| `get_tap()` | Get current tap instance (EntropyTap or HybridTap) |
| `get_pool()` | Get the global entropy pool instance |

## How It Works

Expand All @@ -307,7 +348,7 @@ True random numbers from quantum phenomena:
| v |
| +-----------+ |
| | EXTRACTOR | Secure extraction |
| | (Tap) | Depletion protection |
| | (Tap) | DIRECT or HYBRID mode |
| +-----+-----+ |
| v |
| +---------------+--------------+ |
Expand Down
113 changes: 98 additions & 15 deletions src/trueentropy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,24 @@
# Type Imports (for type hints)
# -----------------------------------------------------------------------------
from collections.abc import MutableSequence, Sequence
from typing import TYPE_CHECKING, Any, TypeVar
from typing import TYPE_CHECKING, Any, Literal, TypeVar

if TYPE_CHECKING:
pass

# -----------------------------------------------------------------------------
# Internal Module Imports
# -----------------------------------------------------------------------------
import trueentropy.config as _config_module
from trueentropy.config import (
TrueEntropyConfig,
configure,
get_config,
reset_config,
)
from trueentropy.health import HealthStatus, entropy_health
from trueentropy.hybrid import HybridTap
from trueentropy.pool import EntropyPool
from trueentropy.tap import EntropyTap
from trueentropy.tap import BaseTap, EntropyTap

# -----------------------------------------------------------------------------
# Type Variables for Generic Functions
Expand All @@ -78,12 +79,96 @@
# Users can also create their own instances if needed.

_pool: EntropyPool = EntropyPool()
_tap: EntropyTap = EntropyTap(_pool)
# _tap is initialized with EntropyTap (DIRECT mode) by default
_tap: BaseTap = EntropyTap(_pool)

# Flag to track if background collector is running
_collector_running: bool = False


# -----------------------------------------------------------------------------
# Configuration Helper
# -----------------------------------------------------------------------------


def _update_tap() -> None:
"""
Update the global _tap instance based on current configuration.

Switches between EntropyTap (DIRECT) and HybridTap (HYBRID).
"""
global _tap
config = get_config()

if config.mode == "HYBRID":
# Create new HybridTap if not already one
if not isinstance(_tap, HybridTap):
_tap = HybridTap(_pool, reseed_interval=config.hybrid_reseed_interval)
else:
# Update existing HybridTap interval
_tap._reseed_interval = config.hybrid_reseed_interval
else:
# Default to DIRECT mode (EntropyTap)
if not isinstance(_tap, EntropyTap):
_tap = EntropyTap(_pool)


def configure(
*,
mode: Literal["DIRECT", "HYBRID"] | None = None,
hybrid_reseed_interval: float | None = None,
offline_mode: bool | None = None,
enable_timing: bool | None = None,
enable_system: bool | None = None,
enable_network: bool | None = None,
enable_external: bool | None = None,
enable_weather: bool | None = None,
enable_radioactive: bool | None = None,
) -> TrueEntropyConfig:
"""
Configure TrueEntropy globally.

This function updates the global configuration and switches operation mode
(DIRECT vs HYBRID) if requested.

Args:
mode: Operation mode ("DIRECT" or "HYBRID")
hybrid_reseed_interval: Seconds between re-seeds in HYBRID mode
offline_mode: If True, disables all network-dependent sources.
enable_timing: Enable/disable CPU timing harvester
enable_system: Enable/disable system state harvester
enable_network: Enable/disable network latency harvester
enable_external: Enable/disable external API harvester
enable_weather: Enable/disable weather data harvester
enable_radioactive: Enable/disable quantum randomness harvester

Returns:
The updated global configuration

Example:
>>> import trueentropy
>>> # Switch to Hybrid Mode (faster)
>>> trueentropy.configure(mode="HYBRID")
"""
# Call the underlying config update
cfg = _config_module.configure(
mode=mode,
hybrid_reseed_interval=hybrid_reseed_interval,
offline_mode=offline_mode,
enable_timing=enable_timing,
enable_system=enable_system,
enable_network=enable_network,
enable_external=enable_external,
enable_weather=enable_weather,
enable_radioactive=enable_radioactive,
)

# Update the active tap based on new config
_update_tap()

return cfg


# =============================================================================
# PUBLIC API - Random Value Generation
# =============================================================================
Expand All @@ -93,9 +178,7 @@ def random() -> float:
"""
Generate a random floating-point number in the range [0.0, 1.0).

This function extracts entropy from the pool and converts it to a
uniformly distributed float. The distribution is uniform, meaning
all values in the range are equally likely.
This function uses the currently configured tap (DIRECT or HYBRID).

Returns:
A float value where 0.0 <= value < 1.0
Expand Down Expand Up @@ -182,9 +265,8 @@ def randbytes(n: int) -> bytes:
"""
Generate n random bytes.

This function extracts raw entropy from the pool and returns it
as a bytes object. Useful for generating cryptographic keys,
tokens, or other binary data.
This function extracts raw entropy (or PRNG bytes in Hybrid mode).
Useful for generating cryptographic keys, tokens, or other binary data.

Args:
n: The number of bytes to generate (must be positive)
Expand All @@ -208,8 +290,7 @@ def shuffle(seq: MutableSequence[Any]) -> None:
"""
Shuffle a mutable sequence in-place.

Uses the Fisher-Yates shuffle algorithm with true random numbers
to ensure a uniform distribution of permutations.
Uses the Fisher-Yates shuffle algorithm.

Args:
seq: A mutable sequence (list) to shuffle in-place
Expand Down Expand Up @@ -503,15 +584,15 @@ def get_pool() -> EntropyPool:
return _pool


def get_tap() -> EntropyTap:
def get_tap() -> BaseTap:
"""
Get the global entropy tap instance.

This is useful for advanced users who want to use the tap
directly or create their own tap with custom settings.
directly or inspect which implementation (BaseTap/HybridTap) is active.

Returns:
The global EntropyTap instance
The global BaseTap instance (EntropyTap or HybridTap)
"""
return _tap

Expand Down Expand Up @@ -557,6 +638,8 @@ def get_tap() -> EntropyTap:
# Classes (for type hints and advanced usage)
"EntropyPool",
"EntropyTap",
"HybridTap",
"BaseTap",
"HealthStatus",
"TrueEntropyConfig",
]
Loading
Loading