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.
Files changed (41) hide show
  1. photorand-1.0.2/.editorconfig +6 -0
  2. photorand-1.0.2/.gitattributes +3 -0
  3. photorand-1.0.2/.gitignore +218 -0
  4. photorand-1.0.2/.python-version +1 -0
  5. photorand-1.0.2/PKG-INFO +152 -0
  6. photorand-1.0.2/README.md +130 -0
  7. photorand-1.0.2/docs/entropy-expansion.md +39 -0
  8. photorand-1.0.2/docs/entropy-extraction.md +38 -0
  9. photorand-1.0.2/pyproject.toml +55 -0
  10. photorand-1.0.2/pyrefly.toml +3 -0
  11. photorand-1.0.2/src/photorand/__init__.py +33 -0
  12. photorand-1.0.2/src/photorand/__main__.py +5 -0
  13. photorand-1.0.2/src/photorand/cli/__main__.py +5 -0
  14. photorand-1.0.2/src/photorand/cli/extract.py +43 -0
  15. photorand-1.0.2/src/photorand/cli/generate.py +59 -0
  16. photorand-1.0.2/src/photorand/cli/main.py +21 -0
  17. photorand-1.0.2/src/photorand/cli/parser.py +114 -0
  18. photorand-1.0.2/src/photorand/high_level/__init__.py +5 -0
  19. photorand-1.0.2/src/photorand/high_level/engine.py +178 -0
  20. photorand-1.0.2/src/photorand/high_level/seed.py +103 -0
  21. photorand-1.0.2/src/photorand/logger.py +27 -0
  22. photorand-1.0.2/src/photorand/low_level/__init__.py +30 -0
  23. photorand-1.0.2/src/photorand/low_level/csprng.py +47 -0
  24. photorand-1.0.2/src/photorand/low_level/formatters.py +101 -0
  25. photorand-1.0.2/src/photorand/low_level/generate.py +35 -0
  26. photorand-1.0.2/src/photorand/low_level/hash.py +23 -0
  27. photorand-1.0.2/src/photorand/low_level/ingest.py +38 -0
  28. photorand-1.0.2/src/photorand/low_level/sample.py +35 -0
  29. photorand-1.0.2/src/photorand/py.typed +0 -0
  30. photorand-1.0.2/tests/cli/test_generate_integration.py +93 -0
  31. photorand-1.0.2/tests/cli/test_main.py +358 -0
  32. photorand-1.0.2/tests/conftest.py +21 -0
  33. photorand-1.0.2/tests/high_level/test_engine.py +120 -0
  34. photorand-1.0.2/tests/high_level/test_seed.py +42 -0
  35. photorand-1.0.2/tests/low_level/test_csprng.py +111 -0
  36. photorand-1.0.2/tests/low_level/test_formatters.py +48 -0
  37. photorand-1.0.2/tests/low_level/test_generate.py +43 -0
  38. photorand-1.0.2/tests/low_level/test_hash.py +24 -0
  39. photorand-1.0.2/tests/low_level/test_ingest.py +33 -0
  40. photorand-1.0.2/tests/low_level/test_sample.py +19 -0
  41. photorand-1.0.2/upload.sh +7 -0
@@ -0,0 +1,6 @@
1
+ # .editorconfig
2
+ root = true
3
+
4
+ [*.py]
5
+ indent_style = tabs
6
+ quote_type = double
@@ -0,0 +1,3 @@
1
+ *.ARW filter=lfs diff=lfs merge=lfs -text
2
+ *.CR2 filter=lfs diff=lfs merge=lfs -text
3
+ examples/**/*.png filter=lfs diff=lfs merge=lfs -text
@@ -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
@@ -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,3 @@
1
+ [pyrefly]
2
+ source_directories = ["src", "tests", "examples"]
3
+ exclude = [".venv", ".git", "**/__pycache__"]
@@ -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
+ ]
@@ -0,0 +1,5 @@
1
+ from .cli.main import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ main()