photorand 1.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- photorand-1.0.2/.editorconfig +6 -0
- photorand-1.0.2/.gitattributes +3 -0
- photorand-1.0.2/.gitignore +218 -0
- photorand-1.0.2/.python-version +1 -0
- photorand-1.0.2/PKG-INFO +152 -0
- photorand-1.0.2/README.md +130 -0
- photorand-1.0.2/docs/entropy-expansion.md +39 -0
- photorand-1.0.2/docs/entropy-extraction.md +38 -0
- photorand-1.0.2/pyproject.toml +55 -0
- photorand-1.0.2/pyrefly.toml +3 -0
- photorand-1.0.2/src/photorand/__init__.py +33 -0
- photorand-1.0.2/src/photorand/__main__.py +5 -0
- photorand-1.0.2/src/photorand/cli/__main__.py +5 -0
- photorand-1.0.2/src/photorand/cli/extract.py +43 -0
- photorand-1.0.2/src/photorand/cli/generate.py +59 -0
- photorand-1.0.2/src/photorand/cli/main.py +21 -0
- photorand-1.0.2/src/photorand/cli/parser.py +114 -0
- photorand-1.0.2/src/photorand/high_level/__init__.py +5 -0
- photorand-1.0.2/src/photorand/high_level/engine.py +178 -0
- photorand-1.0.2/src/photorand/high_level/seed.py +103 -0
- photorand-1.0.2/src/photorand/logger.py +27 -0
- photorand-1.0.2/src/photorand/low_level/__init__.py +30 -0
- photorand-1.0.2/src/photorand/low_level/csprng.py +47 -0
- photorand-1.0.2/src/photorand/low_level/formatters.py +101 -0
- photorand-1.0.2/src/photorand/low_level/generate.py +35 -0
- photorand-1.0.2/src/photorand/low_level/hash.py +23 -0
- photorand-1.0.2/src/photorand/low_level/ingest.py +38 -0
- photorand-1.0.2/src/photorand/low_level/sample.py +35 -0
- photorand-1.0.2/src/photorand/py.typed +0 -0
- photorand-1.0.2/tests/cli/test_generate_integration.py +93 -0
- photorand-1.0.2/tests/cli/test_main.py +358 -0
- photorand-1.0.2/tests/conftest.py +21 -0
- photorand-1.0.2/tests/high_level/test_engine.py +120 -0
- photorand-1.0.2/tests/high_level/test_seed.py +42 -0
- photorand-1.0.2/tests/low_level/test_csprng.py +111 -0
- photorand-1.0.2/tests/low_level/test_formatters.py +48 -0
- photorand-1.0.2/tests/low_level/test_generate.py +43 -0
- photorand-1.0.2/tests/low_level/test_hash.py +24 -0
- photorand-1.0.2/tests/low_level/test_ingest.py +33 -0
- photorand-1.0.2/tests/low_level/test_sample.py +19 -0
- photorand-1.0.2/upload.sh +7 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
|
|
204
|
+
# Ruff stuff:
|
|
205
|
+
.ruff_cache/
|
|
206
|
+
|
|
207
|
+
# PyPI configuration file
|
|
208
|
+
.pypirc
|
|
209
|
+
|
|
210
|
+
# Marimo
|
|
211
|
+
marimo/_static/
|
|
212
|
+
marimo/_lsp/
|
|
213
|
+
__marimo__/
|
|
214
|
+
|
|
215
|
+
# Streamlit
|
|
216
|
+
.streamlit/secrets.toml
|
|
217
|
+
|
|
218
|
+
.vscode/settings.json
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14.2
|
photorand-1.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: photorand
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: A True Random Number Generator using raw camera sensor data.
|
|
5
|
+
Project-URL: Homepage, https://github.com/illescasDaniel/photorand
|
|
6
|
+
Author-email: Daniel Illescas Romero <dev@daniel-ir.eu>
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Requires-Dist: cryptography>=46.0.0
|
|
12
|
+
Requires-Dist: numpy>=2.0.0
|
|
13
|
+
Requires-Dist: rawpy>=0.26.1
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: build; extra == 'dev'
|
|
16
|
+
Requires-Dist: imageio; extra == 'dev'
|
|
17
|
+
Requires-Dist: matplotlib; extra == 'dev'
|
|
18
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
19
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
20
|
+
Requires-Dist: vermin; extra == 'dev'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# photorand
|
|
24
|
+
|
|
25
|
+
A True Random Number Generator (TRNG) using raw camera sensor data to extract physical entropy.
|
|
26
|
+
|
|
27
|
+
## Project Structure
|
|
28
|
+
|
|
29
|
+
- `src/photorand/` - Main package source code.
|
|
30
|
+
- `low_level/` - Modular primitives (ingest, sample, hash, csprng).
|
|
31
|
+
- `high_level/` - Clean OO abstractions (`PhotoRandSeed`, `PhotoRandEngine`).
|
|
32
|
+
- `cli/` - Command-line interface logic.
|
|
33
|
+
- `docs/` - Technical and scientific documentation detailing the [entropy extraction](docs/entropy-extraction.md) and [entropy expansion](docs/entropy-expansion.md) mechanisms.
|
|
34
|
+
- `tests/` - Comprehensive test suite organized by architectural layer.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
This project uses `pyproject.toml` for managing dependencies.
|
|
39
|
+
|
|
40
|
+
1. **Create and activate a virtual environment** (recommended):
|
|
41
|
+
```bash
|
|
42
|
+
python -m venv .venv
|
|
43
|
+
source .venv/bin/activate # On Windows, use `.venv\Scripts\activate`
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
2. **Install the project in editable mode**:
|
|
47
|
+
```bash
|
|
48
|
+
pip install -e .
|
|
49
|
+
# or this one for also installing the dev tools
|
|
50
|
+
pip install -e ".[dev]"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### 1. High-Level Classes (Recommended)
|
|
56
|
+
|
|
57
|
+
The high-level classes provide a stateful and convenient interface for both TRNG and CSPRNG operations.
|
|
58
|
+
|
|
59
|
+
#### `PhotoRandSeed` (TRNG)
|
|
60
|
+
Encapsulates the process of extracting entropy from a RAW image. It is perfect for generating one-off secure seeds, keys, or dice rolls directly from physical noise.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from photorand.high_level.seed import PhotoRandSeed
|
|
64
|
+
|
|
65
|
+
# 1. Extract entropy from a RAW image
|
|
66
|
+
seed = PhotoRandSeed("path/to/image.raw")
|
|
67
|
+
|
|
68
|
+
# 2. Access as bytes, hex, or large integer
|
|
69
|
+
print(seed.to_hex_string())
|
|
70
|
+
print(seed.to_int())
|
|
71
|
+
|
|
72
|
+
# 3. Roll a D100 using physical entropy (Rejection Sampling)
|
|
73
|
+
luck = seed.to_int_range(1, 100)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### `PhotoRandEngine` (CSPRNG)
|
|
77
|
+
An infinite stream generator powered by ChaCha20, seeded by a `PhotoRandSeed`. It handles salting (Time + PID) automatically to ensure that even consecutive runs with the same image produce unique streams.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from photorand.high_level.engine import PhotoRandEngine
|
|
81
|
+
|
|
82
|
+
# 1. Initialize from image or existing Seed object
|
|
83
|
+
engine = PhotoRandEngine("path/to/image.raw")
|
|
84
|
+
|
|
85
|
+
# 2. Pull arbitrary amounts of secure data
|
|
86
|
+
key = engine.next_bytes(32)
|
|
87
|
+
pin = engine.next_string(length=6, charset='numeric')
|
|
88
|
+
dice = engine.next_int_range(1, 20)
|
|
89
|
+
|
|
90
|
+
# 3. Batch generation
|
|
91
|
+
multiple_passwords = engine.generate_batch(engine.next_string, count=5, length=16)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### CLI (Command Line Interface)
|
|
95
|
+
|
|
96
|
+
The package includes a powerful CLI to use these classes directly from your terminal.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Extract 64-byte photorand seed (hex)
|
|
100
|
+
python -m photorand extract path/to/raw_image.ARW
|
|
101
|
+
|
|
102
|
+
# Roll a D20
|
|
103
|
+
python -m photorand extract path/to/raw_image.ARW --format range --min 1 --max 20
|
|
104
|
+
|
|
105
|
+
# Generate 5 random 16-char alphanumeric passwords
|
|
106
|
+
python -m photorand generate path/to/raw_image.ARW --type string -n 5 -l 16 --charset alpha
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
*For more details, run:* `python -m photorand --help`
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### 2. Low-Level Modular Functions
|
|
114
|
+
|
|
115
|
+
For maximum control or research, you can use the modular primitives directly.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from photorand.low_level.ingest import ingest_raw_image
|
|
119
|
+
from photorand.low_level.sample import sample_entropy_grid
|
|
120
|
+
from photorand.low_level.hash import hash_entropy_pool
|
|
121
|
+
|
|
122
|
+
# 1. Ingest raw sensor data
|
|
123
|
+
raw_data = ingest_raw_image("path/to/image.raw")
|
|
124
|
+
|
|
125
|
+
# 2. Extract raw LSB bits
|
|
126
|
+
entropy_pool = sample_entropy_grid(raw_data)
|
|
127
|
+
|
|
128
|
+
# 3. Condition entropy into uniform bytes
|
|
129
|
+
seed_bytes = hash_entropy_pool(entropy_pool)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Alternatively, use the functional pipeline. It accepts custom functions for each part of the algorithm (ingest_fn, sample_fn and hash_fn) although we already provide the values as default parameters:
|
|
133
|
+
```python
|
|
134
|
+
from photorand.low_level.generate import generate_true_random_number
|
|
135
|
+
seed = generate_true_random_number("path/to/image.raw")
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
### Running the Tests
|
|
143
|
+
```bash
|
|
144
|
+
python -m pytest -v --log-cli-level=INFO
|
|
145
|
+
# or just this one to skip logs
|
|
146
|
+
pytest
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Formatting
|
|
150
|
+
```bash
|
|
151
|
+
ruff check --fix src tests examples
|
|
152
|
+
```
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# photorand
|
|
2
|
+
|
|
3
|
+
A True Random Number Generator (TRNG) using raw camera sensor data to extract physical entropy.
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
- `src/photorand/` - Main package source code.
|
|
8
|
+
- `low_level/` - Modular primitives (ingest, sample, hash, csprng).
|
|
9
|
+
- `high_level/` - Clean OO abstractions (`PhotoRandSeed`, `PhotoRandEngine`).
|
|
10
|
+
- `cli/` - Command-line interface logic.
|
|
11
|
+
- `docs/` - Technical and scientific documentation detailing the [entropy extraction](docs/entropy-extraction.md) and [entropy expansion](docs/entropy-expansion.md) mechanisms.
|
|
12
|
+
- `tests/` - Comprehensive test suite organized by architectural layer.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
This project uses `pyproject.toml` for managing dependencies.
|
|
17
|
+
|
|
18
|
+
1. **Create and activate a virtual environment** (recommended):
|
|
19
|
+
```bash
|
|
20
|
+
python -m venv .venv
|
|
21
|
+
source .venv/bin/activate # On Windows, use `.venv\Scripts\activate`
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
2. **Install the project in editable mode**:
|
|
25
|
+
```bash
|
|
26
|
+
pip install -e .
|
|
27
|
+
# or this one for also installing the dev tools
|
|
28
|
+
pip install -e ".[dev]"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
### 1. High-Level Classes (Recommended)
|
|
34
|
+
|
|
35
|
+
The high-level classes provide a stateful and convenient interface for both TRNG and CSPRNG operations.
|
|
36
|
+
|
|
37
|
+
#### `PhotoRandSeed` (TRNG)
|
|
38
|
+
Encapsulates the process of extracting entropy from a RAW image. It is perfect for generating one-off secure seeds, keys, or dice rolls directly from physical noise.
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from photorand.high_level.seed import PhotoRandSeed
|
|
42
|
+
|
|
43
|
+
# 1. Extract entropy from a RAW image
|
|
44
|
+
seed = PhotoRandSeed("path/to/image.raw")
|
|
45
|
+
|
|
46
|
+
# 2. Access as bytes, hex, or large integer
|
|
47
|
+
print(seed.to_hex_string())
|
|
48
|
+
print(seed.to_int())
|
|
49
|
+
|
|
50
|
+
# 3. Roll a D100 using physical entropy (Rejection Sampling)
|
|
51
|
+
luck = seed.to_int_range(1, 100)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### `PhotoRandEngine` (CSPRNG)
|
|
55
|
+
An infinite stream generator powered by ChaCha20, seeded by a `PhotoRandSeed`. It handles salting (Time + PID) automatically to ensure that even consecutive runs with the same image produce unique streams.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from photorand.high_level.engine import PhotoRandEngine
|
|
59
|
+
|
|
60
|
+
# 1. Initialize from image or existing Seed object
|
|
61
|
+
engine = PhotoRandEngine("path/to/image.raw")
|
|
62
|
+
|
|
63
|
+
# 2. Pull arbitrary amounts of secure data
|
|
64
|
+
key = engine.next_bytes(32)
|
|
65
|
+
pin = engine.next_string(length=6, charset='numeric')
|
|
66
|
+
dice = engine.next_int_range(1, 20)
|
|
67
|
+
|
|
68
|
+
# 3. Batch generation
|
|
69
|
+
multiple_passwords = engine.generate_batch(engine.next_string, count=5, length=16)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### CLI (Command Line Interface)
|
|
73
|
+
|
|
74
|
+
The package includes a powerful CLI to use these classes directly from your terminal.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Extract 64-byte photorand seed (hex)
|
|
78
|
+
python -m photorand extract path/to/raw_image.ARW
|
|
79
|
+
|
|
80
|
+
# Roll a D20
|
|
81
|
+
python -m photorand extract path/to/raw_image.ARW --format range --min 1 --max 20
|
|
82
|
+
|
|
83
|
+
# Generate 5 random 16-char alphanumeric passwords
|
|
84
|
+
python -m photorand generate path/to/raw_image.ARW --type string -n 5 -l 16 --charset alpha
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
*For more details, run:* `python -m photorand --help`
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### 2. Low-Level Modular Functions
|
|
92
|
+
|
|
93
|
+
For maximum control or research, you can use the modular primitives directly.
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from photorand.low_level.ingest import ingest_raw_image
|
|
97
|
+
from photorand.low_level.sample import sample_entropy_grid
|
|
98
|
+
from photorand.low_level.hash import hash_entropy_pool
|
|
99
|
+
|
|
100
|
+
# 1. Ingest raw sensor data
|
|
101
|
+
raw_data = ingest_raw_image("path/to/image.raw")
|
|
102
|
+
|
|
103
|
+
# 2. Extract raw LSB bits
|
|
104
|
+
entropy_pool = sample_entropy_grid(raw_data)
|
|
105
|
+
|
|
106
|
+
# 3. Condition entropy into uniform bytes
|
|
107
|
+
seed_bytes = hash_entropy_pool(entropy_pool)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Alternatively, use the functional pipeline. It accepts custom functions for each part of the algorithm (ingest_fn, sample_fn and hash_fn) although we already provide the values as default parameters:
|
|
111
|
+
```python
|
|
112
|
+
from photorand.low_level.generate import generate_true_random_number
|
|
113
|
+
seed = generate_true_random_number("path/to/image.raw")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
### Running the Tests
|
|
121
|
+
```bash
|
|
122
|
+
python -m pytest -v --log-cli-level=INFO
|
|
123
|
+
# or just this one to skip logs
|
|
124
|
+
pytest
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Formatting
|
|
128
|
+
```bash
|
|
129
|
+
ruff check --fix src tests examples
|
|
130
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# The CSPRNG Mechanism: Entropy Expansion
|
|
2
|
+
|
|
3
|
+
This document explains how the physical seed generated via [`photorand`'s extraction process](entropy-extraction.md) is expanded into a continuous, high-volume stream of cryptographically secure random data.
|
|
4
|
+
|
|
5
|
+
## The Need for Expansion
|
|
6
|
+
|
|
7
|
+
While direct physical extraction produces high-quality true randomness, harvesting large volumes (megabytes or gigabytes) directly from a camera sensor is slow and computationally expensive.
|
|
8
|
+
|
|
9
|
+
To solve this, we use a **CSPRNG** (Cryptographically Secure Pseudo-Random Number Generator). The CSPRNG takes a small, high-entropy "seed" from the photorand and expands it into an effectively infinite stream of bytes that is computationally indistinguishable from true randomness.
|
|
10
|
+
|
|
11
|
+
## The Expansion Engine: ChaCha20
|
|
12
|
+
|
|
13
|
+
This project uses the **ChaCha20** stream cipher as its expansion core.
|
|
14
|
+
|
|
15
|
+
### Why ChaCha20?
|
|
16
|
+
* **Security**: ChaCha20 is a modern, high-security cipher designed by Daniel J. Bernstein. It is widely used in protocols like TLS 1.3 and SSH.
|
|
17
|
+
* **Performance**: It is exceptionally fast in software implementations and does not require specialized hardware acceleration (like AES-NI) to be efficient.
|
|
18
|
+
* **No State Leakage**: Unlike some older PRNGs, a CSPRNG based on a modern stream cipher ensures that even if part of the output is compromised, it is computationally impossible to determine past or future outputs.
|
|
19
|
+
|
|
20
|
+
## Implementation Details (`low_level/csprng.py`)
|
|
21
|
+
|
|
22
|
+
1. **Seeding**: The 64-byte output from the photorand is used to initialize the cipher.
|
|
23
|
+
* The first **32 bytes** (256 bits) are used as the **Key**.
|
|
24
|
+
* The next **16 bytes** (128 bits) are used as the **Nonce** (Initialization Vector).
|
|
25
|
+
2. **Keystream Generation**: To generate random bytes, we encrypt a stream of null bytes (`0x00`).
|
|
26
|
+
3. **Result**: The resulting "encrypted" output is a pure keystream of cryptographically secure pseudo-random bytes.
|
|
27
|
+
|
|
28
|
+
## Usage in the CLI
|
|
29
|
+
|
|
30
|
+
When you run the `generate` command:
|
|
31
|
+
```bash
|
|
32
|
+
python -m photorand generate <image_path> [options]
|
|
33
|
+
```
|
|
34
|
+
The tool first runs the photorand process to get a fresh 64-byte seed and then uses this seed to drive the ChaCha20 expander to provide as much data as requested.
|
|
35
|
+
|
|
36
|
+
## Security Properties
|
|
37
|
+
|
|
38
|
+
* **Prediction Resistance**: Given any amount of output from the generator, an adversary with unlimited time but limited classical computing power cannot predict the next bit better than a coin flip (50/50).
|
|
39
|
+
* **Backtracking Resistance**: If the internal state of the generator is compromised at a certain point, previous outputs remain secure.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# The `photorand` Mechanism: Physical Entropy Extraction
|
|
2
|
+
|
|
3
|
+
This document outlines the scientific and technical architecture of `photorand`. It details how the system leverages raw camera sensor data to extract physical entropy, functioning as a True Random Number Generator (TRNG).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The photorand extracts physical entropy from raw camera sensor data. Unlike typical pseudo-random number generators (PRNGs) that rely on mathematical algorithms starting from a seed, this photorand leverages the inherent physical noise present in digital image sensors.
|
|
8
|
+
|
|
9
|
+
## Entropy Source: Raw Sensor Noise
|
|
10
|
+
|
|
11
|
+
Digital image sensors (CMOS/CCD) are subject to various forms of physical noise, even when no light is present or when capturing a static scene. Key sources of this noise include:
|
|
12
|
+
|
|
13
|
+
* **Shot Noise**: Random fluctuations in the number of photons hitting the sensor.
|
|
14
|
+
* **Read Noise**: Electronic noise introduced during the digitization of the analog signal.
|
|
15
|
+
* **Thermal Noise (Dark Current)**: Noise caused by the thermal agitation of electrons within the sensor.
|
|
16
|
+
|
|
17
|
+
By reading the **RAW** data (before any processing like denoising, demosaicing, or compression), we can isolate these tiny, unpredictable fluctuations.
|
|
18
|
+
|
|
19
|
+
## Implementation Details
|
|
20
|
+
|
|
21
|
+
### 1. Data Ingestion (`low_level/ingest.py`)
|
|
22
|
+
We use the `rawpy` library to access the unprocessed sensor grid. This ensures we are working with the "pure" values captured by the hardware, which contain the most entropy.
|
|
23
|
+
|
|
24
|
+
### 2. Sampling and Isolation (`low_level/sample.py`)
|
|
25
|
+
To extract the most random components and reduce spatial correlation (where neighboring pixels might share similar noise characteristics), we perform two steps:
|
|
26
|
+
|
|
27
|
+
* **Grid Sampling**: We sample pixels at a fixed stride (e.g., every 64th pixel). This spreads the sampling across the entire sensor surface.
|
|
28
|
+
* **LSB Extraction**: We isolate the 4 **Least Significant Bits (LSBs)** of each sampled pixel value. The higher-order bits represent the actual image content (which is predictable), while the LSBs are dominated by the physical noise floor.
|
|
29
|
+
|
|
30
|
+
### 3. Entropy Conditioning (`low_level/hash.py`)
|
|
31
|
+
The raw "lsb-pool" extracted from the sensor might still have small statistical biases. To produce a perfectly uniform output, we pass the extracted bytes through a **cryptographic hash function**.
|
|
32
|
+
|
|
33
|
+
* **Algorithm**: SHA3-512
|
|
34
|
+
* **Purpose**: SHA3-512 acts as an "entropy blender." It compresses the large pool of harvested noise into a 64-byte (512-bit) digest. The avalanche effect of the hash ensures that any small amount of entropy in the input is distributed uniformly across the entire output block.
|
|
35
|
+
|
|
36
|
+
## Conclusion
|
|
37
|
+
|
|
38
|
+
The result is a 64-byte "seed" of true randomness, rooted in the physical reality of the camera sensor's environment. This seed is highly secure and suitable for use in cryptographic applications or as a source for a CSPRNG.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "photorand"
|
|
7
|
+
version = "1.0.2"
|
|
8
|
+
authors = [{ name = "Daniel Illescas Romero", email = "dev@daniel-ir.eu" }]
|
|
9
|
+
description = "A True Random Number Generator using raw camera sensor data."
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
dependencies = ["rawpy>=0.26.1", "numpy>=2.0.0", "cryptography>=46.0.0"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.scripts]
|
|
20
|
+
photorand = "photorand.cli.main:main"
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/illescasDaniel/photorand"
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
dev = ["pytest", "ruff", "vermin", "build", "imageio", "matplotlib"]
|
|
27
|
+
|
|
28
|
+
# --- RUFF (Linter & Import Sorter) ---
|
|
29
|
+
[tool.ruff]
|
|
30
|
+
line-length = 160
|
|
31
|
+
target-version = 'py312'
|
|
32
|
+
src = ['src']
|
|
33
|
+
|
|
34
|
+
[tool.ruff.lint]
|
|
35
|
+
# Enable specific rules:
|
|
36
|
+
# F = Pyflakes (Finds unused imports/variables)
|
|
37
|
+
# I = isort (Sorts imports)
|
|
38
|
+
# E = pycodestyle errors
|
|
39
|
+
select = ['F', 'I', 'E']
|
|
40
|
+
# Allow fixing for all enabled rules (imports will be removed/sorted automatically)
|
|
41
|
+
fixable = ['ALL']
|
|
42
|
+
# --- RUFF end ---
|
|
43
|
+
|
|
44
|
+
[tool.ruff.lint.isort]
|
|
45
|
+
# Ensure compatibility with YAPF's vertical style
|
|
46
|
+
combine-as-imports = true
|
|
47
|
+
lines-after-imports = 2
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
log_cli = true
|
|
51
|
+
log_cli_level = "WARNING"
|
|
52
|
+
log_cli_format = "[%(levelname)s] %(message)s"
|
|
53
|
+
|
|
54
|
+
[tool.hatch.build]
|
|
55
|
+
exclude = ["examples", "tests/data"]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from .high_level import PhotoRandEngine as PhotoRandEngine, PhotoRandSeed as PhotoRandSeed
|
|
2
|
+
from .low_level import (
|
|
3
|
+
csprng_format_bytes as csprng_format_bytes,
|
|
4
|
+
csprng_format_int as csprng_format_int,
|
|
5
|
+
csprng_format_range as csprng_format_range,
|
|
6
|
+
csprng_format_string as csprng_format_string,
|
|
7
|
+
expand_entropy_chacha20 as expand_entropy_chacha20,
|
|
8
|
+
generate_true_random_number as generate_true_random_number,
|
|
9
|
+
hash_entropy_pool as hash_entropy_pool,
|
|
10
|
+
ingest_raw_image as ingest_raw_image,
|
|
11
|
+
sample_entropy_grid as sample_entropy_grid,
|
|
12
|
+
trng_format_hex as trng_format_hex,
|
|
13
|
+
trng_format_int as trng_format_int,
|
|
14
|
+
trng_format_range as trng_format_range,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"PhotoRandEngine",
|
|
20
|
+
"PhotoRandSeed",
|
|
21
|
+
"csprng_format_bytes",
|
|
22
|
+
"csprng_format_int",
|
|
23
|
+
"csprng_format_range",
|
|
24
|
+
"csprng_format_string",
|
|
25
|
+
"expand_entropy_chacha20",
|
|
26
|
+
"generate_true_random_number",
|
|
27
|
+
"hash_entropy_pool",
|
|
28
|
+
"ingest_raw_image",
|
|
29
|
+
"sample_entropy_grid",
|
|
30
|
+
"trng_format_hex",
|
|
31
|
+
"trng_format_int",
|
|
32
|
+
"trng_format_range",
|
|
33
|
+
]
|