vqrng 0.4.1__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 (45) hide show
  1. vqrng-0.4.1/LICENSE +21 -0
  2. vqrng-0.4.1/PKG-INFO +431 -0
  3. vqrng-0.4.1/README.md +400 -0
  4. vqrng-0.4.1/pyproject.toml +64 -0
  5. vqrng-0.4.1/setup.cfg +4 -0
  6. vqrng-0.4.1/src/vqrng/__init__.py +29 -0
  7. vqrng-0.4.1/src/vqrng/backends/__init__.py +15 -0
  8. vqrng-0.4.1/src/vqrng/backends/aer.py +19 -0
  9. vqrng-0.4.1/src/vqrng/backends/base.py +104 -0
  10. vqrng-0.4.1/src/vqrng/backends/ibm.py +303 -0
  11. vqrng-0.4.1/src/vqrng/cli.py +449 -0
  12. vqrng-0.4.1/src/vqrng/core.py +452 -0
  13. vqrng-0.4.1/src/vqrng/evidence.py +95 -0
  14. vqrng-0.4.1/src/vqrng/extractor.py +76 -0
  15. vqrng-0.4.1/src/vqrng/health.py +114 -0
  16. vqrng-0.4.1/src/vqrng/qseed.py +81 -0
  17. vqrng-0.4.1/src/vqrng/quantum/__init__.py +5 -0
  18. vqrng-0.4.1/src/vqrng/quantum/chsh.py +41 -0
  19. vqrng-0.4.1/src/vqrng/signing.py +132 -0
  20. vqrng-0.4.1/src/vqrng/verifier/__init__.py +148 -0
  21. vqrng-0.4.1/src/vqrng/verifier/ibm_live.py +163 -0
  22. vqrng-0.4.1/src/vqrng/verifier/level_a.py +170 -0
  23. vqrng-0.4.1/src/vqrng/verifier/level_b.py +227 -0
  24. vqrng-0.4.1/src/vqrng/verifier/level_c.py +126 -0
  25. vqrng-0.4.1/src/vqrng.egg-info/PKG-INFO +431 -0
  26. vqrng-0.4.1/src/vqrng.egg-info/SOURCES.txt +43 -0
  27. vqrng-0.4.1/src/vqrng.egg-info/dependency_links.txt +1 -0
  28. vqrng-0.4.1/src/vqrng.egg-info/entry_points.txt +2 -0
  29. vqrng-0.4.1/src/vqrng.egg-info/requires.txt +12 -0
  30. vqrng-0.4.1/src/vqrng.egg-info/top_level.txt +1 -0
  31. vqrng-0.4.1/tests/test_boundary_cli.py +136 -0
  32. vqrng-0.4.1/tests/test_boundary_sdk.py +150 -0
  33. vqrng-0.4.1/tests/test_chsh.py +386 -0
  34. vqrng-0.4.1/tests/test_cli.py +223 -0
  35. vqrng-0.4.1/tests/test_core.py +80 -0
  36. vqrng-0.4.1/tests/test_evidence_integrity.py +295 -0
  37. vqrng-0.4.1/tests/test_extractor.py +195 -0
  38. vqrng-0.4.1/tests/test_generate.py +129 -0
  39. vqrng-0.4.1/tests/test_health.py +207 -0
  40. vqrng-0.4.1/tests/test_ibm_backend.py +617 -0
  41. vqrng-0.4.1/tests/test_ibm_live.py +312 -0
  42. vqrng-0.4.1/tests/test_qseed.py +342 -0
  43. vqrng-0.4.1/tests/test_remaining_branches.py +163 -0
  44. vqrng-0.4.1/tests/test_signing.py +239 -0
  45. vqrng-0.4.1/tests/test_verify_level_a.py +163 -0
vqrng-0.4.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Frankie Li
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
vqrng-0.4.1/PKG-INFO ADDED
@@ -0,0 +1,431 @@
1
+ Metadata-Version: 2.4
2
+ Name: vqrng
3
+ Version: 0.4.1
4
+ Summary: Research-grade verifiable quantum random number generator: Python SDK and Unix CLI built with Qiskit.
5
+ Author: Frankie Li
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Nzivsqm618/verifiable-qrng
8
+ Project-URL: Repository, https://github.com/Nzivsqm618/verifiable-qrng
9
+ Project-URL: Issues, https://github.com/Nzivsqm618/verifiable-qrng/issues
10
+ Keywords: qiskit,quantum,entropy,qrng,chsh,research
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Scientific/Engineering :: Physics
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: qiskit>=1.0.0
21
+ Requires-Dist: qiskit-ibm-runtime>=0.20.0
22
+ Requires-Dist: qiskit-aer>=0.13.0
23
+ Requires-Dist: numpy>=1.24.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
26
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
27
+ Requires-Dist: cryptography>=41.0.0; extra == "dev"
28
+ Provides-Extra: sign
29
+ Requires-Dist: cryptography>=41.0.0; extra == "sign"
30
+ Dynamic: license-file
31
+
32
+ # Verifiable Quantum Random Number Generator (`vqrng`)
33
+
34
+ [![Python package](https://github.com/Nzivsqm618/verifiable-qrng/actions/workflows/python-package.yml/badge.svg)](https://github.com/Nzivsqm618/verifiable-qrng/actions/workflows/python-package.yml)
35
+ [![Qiskit](https://img.shields.io/badge/Qiskit-1.x-6929C4?logo=qiskit&logoColor=white)](https://qiskit.org/)
36
+ [![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/)
37
+ [![CLI Tool](https://img.shields.io/badge/CLI-vqrng-green.svg)](https://github.com/Nzivsqm618/verifiable-qrng)
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
39
+
40
+ An open-source Python library and Unix CLI tool (`vqrng`) for generating unbiased random integers from quantum measurements with Qiskit, together with an evidence record that can be audited offline.
41
+
42
+ `vqrng` combines **continuous health tests** on the raw bits, **HMAC-SHA256 conditioning**, **unbiased rejection sampling**, **tamper-evident JSON evidence** with optional **Ed25519 signatures**, an optional **CHSH (Bell inequality) spot-check** run alongside the pool, and an optional **live check against the IBM jobs** a record names. **QSeed** expands one recorded seed into a fast local PCG64 stream for simulations.
43
+
44
+ **⚠️ Status: Educational & Research-grade Verifiable QRNG Framework - Not ready for production cryptographic key generation.**
45
+
46
+ ---
47
+
48
+ ## Executive Summary & Goals
49
+
50
+ Classical Pseudo-Random Number Generators (PRNGs) and unverified Hardware RNGs rely on opaque physical noise or deterministic seed states. `vqrng` is a dual-interface (SDK + CLI) framework for studying verifiable quantum randomness:
51
+
52
+ * **Health Tests, Conditioning, then Unbiased Integer Conversion:** Raw measurement bits must pass the SP 800-90B Repetition Count and Adaptive Proportion tests, are then conditioned with HMAC-SHA256, and are mapped to `[min, max]` by strict rejection sampling (n<sub>bits</sub> = ⌈log<sub>2</sub>(range_size)⌉), so the mapping adds no modulo bias.
53
+ * **Tamper-Evident Provenance:** Records every raw shot, the conditioned candidates, rejected candidates, circuit hashes, and a canonical SHA-256 `pool_hash` for offline auditing. An optional Ed25519 signature ties the record to a key you trust.
54
+ * **Dual Execution Modes:** Local testing with the `qiskit-aer` simulator, and execution on physical IBM Quantum QPUs.
55
+ * **Graduated Verification Hierarchy:** Reproducible conversion (Level A), self-consistency and optional signature checks (Level B), and a near-real-time CHSH spot-check (Level C), all offline, plus an optional live comparison with IBM's copy of the jobs.
56
+
57
+ ---
58
+
59
+ ## Key Features
60
+
61
+ * **Python SDK & Unix Pipeline CLI:** Import `vqrng` directly inside Python projects or chain `vqrng` in Unix shell pipelines. Stdout is plain numbers by default, one per line. Pass `-r` for one space-separated line, or `-j` for one compact canonical JSON line.
62
+ * **Continuous Health Tests:** Every batch of raw bits goes through the SP 800-90B Repetition Count Test and Adaptive Proportion Test before conditioning. A stuck or grossly biased source stops the run, and the failure is kept in the evidence.
63
+ * **HMAC-SHA256 Entropy Conditioning:** Every 512 raw bits are conditioned into 256 output bits before rejection sampling, using only the Python standard library (`hmac`, `hashlib`).
64
+ * **Strict Rejection Sampling:** Maps conditioned bits to any `[min, max]` range with no modulo bias.
65
+ * **QPU Budget Control:** `-t` stops `vqrng` from submitting another IBM job once the reported QPU time is used. It is not a cap IBM enforces, and queue time is recorded separately from QPU time.
66
+ * **Offline Verification Engine:** `vqrng verify` audits evidence files, detects edits made after generation, replays the health tests, and checks signatures against keys you pass with `--trusted-key`.
67
+ * **Live IBM Check:** `vqrng verify --ibm` asks IBM, with your own token, whether the jobs named in the evidence still report the same backend, execution window, shots, and submitted circuits.
68
+ * **CHSH Spot-Check:** Optional Bell test with non-orthogonal measurement bases (A<sub>0</sub>, A<sub>1</sub>, B<sub>0</sub>, B<sub>1</sub>), run in the same job as the first pool batch. Level C checks *S* > 2 and that the runs happened within 10 seconds of the pool, on the same backend. `--require-chsh` makes a missing spot-check fail.
69
+ * **QSeed:** `vqrng seed` collects one 32-byte seed with full evidence, and `vqrng expand` turns it into millions of numbers locally with NumPy's PCG64. The expanded numbers are classical and replayable by anyone holding the record.
70
+
71
+ ---
72
+
73
+ ## Installation
74
+
75
+ ```bash
76
+ # Clone the repository
77
+ git clone https://github.com/Nzivsqm618/verifiable-qrng.git
78
+ cd verifiable-qrng
79
+
80
+ # Install library and CLI executable in editable mode
81
+ pip install -e .
82
+
83
+ # Add signing support (Ed25519 via the `cryptography` package)
84
+ pip install -e ".[sign]"
85
+ ```
86
+
87
+ Verification, including signature checks, needs only the standard library. Only signing needs `cryptography`.
88
+
89
+ ---
90
+
91
+ ## Quick Start: CLI Usage
92
+
93
+ By default, `vqrng` writes plain random numbers to stdout (`74`, or one number per line for a pool) so the output is ready for shell scripts and pipes. Logs and status updates go to stderr. Pass `-r` / `--raw` to print a pool on one space-separated line, or `-j` / `--json` to write one compact canonical JSON line (sorted keys, no extra whitespace), the same encoding used for `pool_hash`.
94
+
95
+ Help is `--help`. `-h` selects IBM Quantum hardware, not help.
96
+
97
+ ### 1. Basic Generation (Simulator Mode)
98
+
99
+ Generate a single random integer between 1 and 100 using Qiskit Aer:
100
+
101
+ ```bash
102
+ vqrng -s 1 100
103
+ ```
104
+
105
+ **The Aer backend (`-s`, the default) is a simulator.** Its "measurements" come from a C++ pseudo-random number generator (PRNG), not a quantum process. Use it for local testing and development only. Its output is not quantum randomness.
106
+
107
+ ### 2. Generate a Random Pool
108
+
109
+ Generate a pool of 20 random numbers in the range [1, 1000]:
110
+
111
+ ```bash
112
+ vqrng -s -p 20 1 1000
113
+ ```
114
+
115
+ ### 3. Run on IBM Quantum Hardware
116
+
117
+ Submit to physical IBM QPU hardware, and stop submitting further jobs after 300 seconds of reported QPU time:
118
+
119
+ ```bash
120
+ export IBMQ_API_TOKEN="your_ibm_quantum_api_token"
121
+ vqrng -h -t 300 -p 10 1 100
122
+
123
+ # N-digit number on hardware, pinned to a specific QPU
124
+ vqrng -h -t 300 --backend ibm_torino -d 6 --pad
125
+ ```
126
+
127
+ `vqrng` uses the least busy operational QPU unless `--backend` is given. Job status (`QUEUED`, `RUNNING`, `DONE`, `ERROR`, `CANCELLED`) is reported on stderr with timestamps, so stdout carries only the numbers or JSON. The evidence records `quantum_seconds` (QPU time reported by IBM), `charged_seconds` (what was counted against `-t`), `queue_seconds`, `wall_seconds`, the IBM `job_ids`, and each job's `started_at` / `finished_at` time. Pressing Ctrl+C while a job is waiting cancels it so it does not use more QPU time.
128
+
129
+ **`-t` is not an IBM billing cap.** Using `-h` prints a warning. IBM bills the QPU seconds a job actually uses, and one job can cost more than `-t` (a `-t 2` job can be billed 3 seconds). When that happens the evidence sets `"budget_exceeded": true` and `vqrng` warns on stderr. `-t` only stops `vqrng` from submitting a further job once the reported time is used up. `vqrng` does not set IBM's execution-time limit on the job: when that limit trips, IBM cancels the job and still charges the time already used, so the credit is spent and no numbers come back.
130
+
131
+ When IBM does not report a job's final QPU usage, `vqrng` counts the whole remaining `-t` budget against further jobs, so it does not submit another one. If generation stops early (budget exhausted, a job ends in `ERROR` or `CANCELLED`, or the job can no longer be polled), the command exits with status 1 and prints the job ids on stderr. With `-j`, it also prints the partial evidence (`"status": "partial"`) on stdout, so values and shots already paid for are kept.
132
+
133
+ ### 4. JSON Evidence and Verification
134
+
135
+ Write the evidence payload, pipe it into the verifier, or audit a saved file. Verification requires the JSON payload, so generation commands that feed `vqrng verify` must include `-j`.
136
+
137
+ ```bash
138
+ # One compact canonical JSON line on stdout
139
+ vqrng -s -j 1 100
140
+
141
+ # Pipeline verification
142
+ vqrng -s -j -p 50 1 1000 | vqrng verify
143
+
144
+ # File verification (UTF-8, or UTF-16 as written by Windows PowerShell's `>`)
145
+ vqrng verify evidence.json
146
+ ```
147
+
148
+ `vqrng verify` reports each level separately and exits with status 1 if any level that ran fails:
149
+
150
+ ```text
151
+ PASS: Level A (reproducible conversion)
152
+ PASS: Level B (tamper-evident provenance) via checksum only (self-consistent; not authenticated)
153
+ Level C (near-real-time CHSH spot-check): PASSED (S = 2.82)
154
+ ```
155
+
156
+ Level C runs only when the evidence was generated with `--chsh`. Otherwise its line is `SKIP: Level C (near-real-time CHSH spot-check) was not run; the evidence has no chsh_data`, and the record can still verify. Pass `vqrng verify --require-chsh` to make a missing spot-check fail instead.
157
+
158
+ The evidence `tape` records every raw measured shot of every job, including the unused tail of the last batch. `health` records the health-test results over that raw stream, and `extractor` the conditioning parameters. Each item's `bitstring` and `rejected` entries are conditioned candidates, and Level B recomputes the health results and the candidates from the raw tape. On IBM hardware, each tape batch also records `isa_sha256`, the SHA-256 of the transpiled circuit actually submitted. `pool_hash` is the SHA-256 of the canonical JSON of the whole payload except `pool_hash`, `signature`, and `public_key`. `kind` is `"pool"` for numbers and `"seed"` for a QSeed record. Only evidence format version `"4"` verifies.
159
+
160
+ ### 5. Health Tests
161
+
162
+ Before a batch of raw bits is conditioned, it goes through the two continuous health tests of NIST SP 800-90B section 4.4, for a binary source:
163
+
164
+ | Test | Fails when | Cutoff |
165
+ | --- | --- | --- |
166
+ | Repetition Count Test | The same raw bit repeats 41 times in a row, across shots and batches | 41 |
167
+ | Adaptive Proportion Test | In a 1024-bit window, 793 or more bits equal the window's first bit | 793 |
168
+
169
+ The cutoffs follow from a false-positive rate α = 2<sup>−20</sup> and **an assumed** min-entropy of H<sub>min</sub> = 0.5 bits per raw bit, the same assumption the 2:1 HMAC conditioning relies on. `vqrng` does not measure H<sub>min</sub>. When a test fails, sampling stops, the failing batch stays on the tape with the error, and the command exits with status 1. When fewer than 1024 raw bits were measured, `health.apt` is `"not_enough_bits"`: the proportion test did not run.
170
+
171
+ These tests are a fault alarm, not NIST validation. They catch a stuck or grossly biased qubit. A good PRNG, including the Aer simulator, passes them, and so does a perfectly predictable source such as `0101…`.
172
+
173
+ ### 6. Live IBM Check
174
+
175
+ ```bash
176
+ vqrng verify --ibm evidence.json
177
+ ```
178
+
179
+ `--ibm` uses `IBMQ_API_TOKEN` or `QISKIT_IBM_TOKEN` to load every job named on the tape. For each job it compares status (`DONE`), backend name, whether the backend is a simulator, IBM's execution window, the pool shots, the CHSH counts, and, while IBM still returns them, the hashes of the submitted circuits. If IBM no longer has a job, the check fails. When IBM no longer returns the submitted circuits, the check says so in a `NOTE:` line instead of comparing their hashes. Offline `vqrng verify` never contacts IBM.
180
+
181
+ A pass shows that the jobs this token can see still match the evidence. It is not hardware attestation: IBM is still trusted for the shots, and a verifier who cannot see those jobs cannot run the check. For third parties without access to the jobs, sign the evidence.
182
+
183
+ ### 7. Signed Evidence
184
+
185
+ A checksum only shows that a record is self-consistent. Anyone who edits a record can recompute `pool_hash`. To tie evidence to its producer, sign it with an Ed25519 key, and have verifiers check it against the public key they already trust:
186
+
187
+ ```bash
188
+ # Create a key once, keep signing.key secret, and publish the public key
189
+ python -c "from vqrng.signing import generate_signing_key as g; print(g())" > signing.key
190
+ python -c "from vqrng.signing import public_key_hex as p; print(p(open('signing.key').read()))"
191
+
192
+ # Sign while generating
193
+ vqrng -j --sign-key signing.key 1 100 > evidence.json
194
+
195
+ # Authenticate against the published key
196
+ vqrng verify --trusted-key <PUBLIC_KEY_HEX> evidence.json
197
+ ```
198
+
199
+ The payload then carries `signature` and `public_key` (hex). Level B reports one of three outcomes:
200
+
201
+ | Level B outcome | Meaning |
202
+ | --- | --- |
203
+ | `via checksum only (self-consistent; not authenticated)` | Unsigned. The hashes match, but anyone could have produced the record. |
204
+ | `via Ed25519 signature from an untrusted key` | Validly signed, but by a key carried in the payload itself. A forger can sign with a key of their own, so this is no stronger than a checksum. |
205
+ | `via Ed25519 signature from a trusted key (authentic)` | Signed by a key passed with `--trusted-key`. |
206
+
207
+ With `--trusted-key`, Level B fails unless the evidence is signed by one of those keys.
208
+
209
+ ### 8. QSeed: Fast Local Expansion of a Recorded Seed
210
+
211
+ Each QPU call costs queue time and credit, so calling `vqrng` inside a hot loop is impractical. QSeed spends one seed job and expands it locally:
212
+
213
+ ```bash
214
+ # Collect a 32-byte seed on IBM hardware, with the usual evidence (always printed as JSON)
215
+ vqrng seed -h -t 30 --chsh --sign-key signing.key > seed.json
216
+ vqrng verify --require-chsh --trusted-key <PUBLIC_KEY_HEX> seed.json
217
+
218
+ # Expand it: one million integers in [1, 100], computed locally with PCG64
219
+ vqrng expand seed.json -p 1000000 1 100
220
+
221
+ # For testing, a simulator seed needs --allow-simulator to expand
222
+ vqrng seed -s > sim-seed.json
223
+ vqrng expand sim-seed.json --allow-simulator -p 10 1 100
224
+ ```
225
+
226
+ `vqrng seed` runs the normal pipeline (health tests, conditioning, shot tape, optional CHSH and signature) for 32 values over [0, 255], an exact 8-bit range, so nothing is rejected and the values are the seed bytes. The record has `kind: "seed"`, the `seed` as hex, and `expander: "numpy.random.PCG64"`. Levels A–C and `--ibm` verify it like any record. Level A also checks that `seed` equals the item bytes. `-s` or `-h` must be given explicitly.
227
+
228
+ `vqrng expand` refuses a record whose `pool_hash` no longer matches, one without a complete seed, and a simulator seed unless `--allow-simulator` is passed. It does not run `vqrng verify`, so verify the record first. Bounds are inclusive, like the rest of `vqrng`. `-r` prints one space-separated line.
229
+
230
+ **What QSeed output is not.** The expanded numbers are PCG64 output, classical and deterministic from the seed. The seed is in the evidence on purpose, so anyone holding the record can replay the whole stream. It is not a CSPRNG and not quantum output. Use it for Monte Carlo, simulation, and test data, never for keys, tokens, or anything an adversary must not predict. Replaying a stream exactly also needs the same NumPy version.
231
+
232
+ ### 9. N-Digit Numbers
233
+
234
+ Generate an N-digit number without setting `MIN_VAL` and `MAX_VAL` by hand:
235
+
236
+ ```bash
237
+ # 6-digit number in [100000, 999999]
238
+ vqrng -s -d 6
239
+
240
+ # Zero-padded 6-character string from "000000" to "999999"
241
+ vqrng -s -d 6 --pad
242
+ ```
243
+
244
+ ---
245
+
246
+ ## CLI Flags
247
+
248
+ ### `-j`, `--json` (Evidence Payload Output)
249
+
250
+ Running `vqrng` without `-j` prints only plain random numbers. Supplying `-j` or `--json` prints one compact canonical JSON line (sorted keys, no extra whitespace). That is the same encoding used for `pool_hash`.
251
+
252
+ Use it for verification logging, audit trails, piping into `vqrng verify`, and storing provenance records. `-j` cannot be combined with `-r`.
253
+
254
+ ### `-r`, `--raw` (Single-Line Output)
255
+
256
+ Prints the pool as space-separated values on one line, for example `741829 938201`. The default remains one value per line. `-r` cannot be combined with `-j`.
257
+
258
+ ```bash
259
+ vqrng -s -r -p 3 1 100
260
+ ```
261
+
262
+ ### `--chsh` (Near-Real-Time CHSH Spot-Check)
263
+
264
+ Adds four Bell-state circuits, one per measurement setting (Alice at 0 or π/2, Bob at π/4 or −π/4), 1024 shots each, to the **first pool job**. On IBM hardware they are extra circuits in the same Sampler job, so they share its queue slot, calibration, and execution window. `chsh_data` records the outcome counts and, per setting, the job id, backend, and `started_at` / `finished_at` time. `pool_hash` covers all of it:
265
+
266
+ ```bash
267
+ vqrng --chsh -j 1 100 | vqrng verify
268
+ ```
269
+
270
+ Level C computes each correlation E = (N<sub>00</sub> + N<sub>11</sub> − N<sub>01</sub> − N<sub>10</sub>) / N and S = |E(A0,B0) + E(A0,B1) + E(A1,B0) − E(A1,B1)|. It passes when all of the following hold:
271
+
272
+ * S > 2, the classical limit. The quantum maximum is 2√2 ≈ 2.83.
273
+ * Every CHSH run executed on the same backend as the pool.
274
+ * Every CHSH run executed within 10 seconds of a pool batch. A run with missing or invalid times counts as decoupled and fails.
275
+
276
+ If the CHSH job fails, the command exits with status 1, and `chsh_data.error` says why.
277
+
278
+ ### `--sign-key FILE`
279
+
280
+ Signs `pool_hash` with the hex Ed25519 private key seed in `FILE` and adds `signature` and `public_key` to the evidence. It needs `pip install vqrng[sign]`. A bad key is rejected before any job is submitted.
281
+
282
+ ### `vqrng verify --trusted-key HEX`
283
+
284
+ Trusts the given hex Ed25519 public key. The flag can be repeated. When it is given, Level B passes only for evidence signed by one of these keys, and reports it as authentic.
285
+
286
+ ### `vqrng verify --require-chsh`
287
+
288
+ Makes Level C fail, instead of being skipped, when the evidence has no `chsh_data`.
289
+
290
+ ### `vqrng verify --ibm`
291
+
292
+ Adds the live IBM job check described above. It needs an IBM token that can see the jobs, and it is the only verification step that uses the network.
293
+
294
+ ### `vqrng seed` and `vqrng expand`
295
+
296
+ `vqrng seed (-s | -h -t N) [--backend NAME] [--chsh] [--sign-key FILE]` collects a seed record and prints it as canonical JSON, including partial evidence when the job fails. `vqrng expand FILE [-p N] [-r] [--allow-simulator] MIN_VAL MAX_VAL` prints numbers from its PCG64 stream. `FILE` can be `-` for stdin.
297
+
298
+ ### `--help`
299
+
300
+ Prints the generated usage text and exits. `-h` / `--hardware` selects IBM Quantum hardware and requires `-t` / `--runtime`. `-t` stops further jobs after that many reported QPU seconds. It does not cap what IBM bills for the job already submitted.
301
+
302
+ ### `-d`, `--digits INTEGER` (N-Digit Shortcut)
303
+
304
+ Generates N-digit numbers without a manual `[min, max]` range.
305
+
306
+ * **Standard mode (`-d N`):** Sets `min_val = 10^(N-1)` and `max_val = 10^N - 1`. For example, `-d 6` generates a number from `100000` to `999999`.
307
+ * **Zero-padded mode (`-d N --pad`):** Sets `min_val = 0` and `max_val = 10^N - 1`, and prints each value as an N-character string with leading zeros. For example, `-d 6 --pad` generates strings from `"000000"` to `"999999"`, such as `"004819"`.
308
+
309
+ **Do not use `-d` output as production 2FA codes, PINs, seeds, or keys.** The default backend is a simulator PRNG. On hardware, the conditioning assumes a min-entropy that is never measured; the health tests only catch gross faults. Output is written to stdout and to plaintext evidence. Production key generation needs an assessed entropy source and protected hardware signing keys, which `vqrng` does not provide. Use `-d` for demonstrations, research, and test data.
310
+
311
+ ---
312
+
313
+ ## Quick Start: Python SDK
314
+
315
+ You can also import `vqrng` directly into Python applications.
316
+
317
+ ### Generation
318
+
319
+ ```python
320
+ import vqrng
321
+
322
+ # Generate a pool of 10 random integers using Qiskit Aer (a PRNG simulator, for testing)
323
+ evidence = vqrng.generate(
324
+ min_val=1,
325
+ max_val=100,
326
+ mode="aer",
327
+ pool_size=10,
328
+ chsh=True, # optional CHSH spot-check in the first job
329
+ signing_key=None, # optional hex Ed25519 private key seed
330
+ )
331
+
332
+ print(f"Generated Numbers: {[item['number'] for item in evidence['items']]}")
333
+ print(f"Canonical Pool Hash: {evidence['pool_hash']}")
334
+ ```
335
+
336
+ If sampling stops before the pool fills (a health-test failure included), or the CHSH test does not finish, `generate` raises `vqrng.GenerationError`. Its `evidence` attribute holds the payload measured so far. A health-test failure has a `vqrng.EntropyHealthError` as its cause:
337
+
338
+ ```python
339
+ try:
340
+ evidence = vqrng.generate(1, 100, mode="hardware", runtime_limit=300, pool_size=50)
341
+ except vqrng.GenerationError as exc:
342
+ evidence = exc.evidence # status == "partial"; accepted values, job ids, and shots so far
343
+ ```
344
+
345
+ `backend` accepts either an IBM QPU name (hardware mode) or a `vqrng.BaseBackend` instance to run on instead of the default. A backend can override `run_many` to put several circuits in one job.
346
+
347
+ The conditioning step is available on its own:
348
+
349
+ ```python
350
+ digest = vqrng.extract_entropy("0110" * 128) # 32-byte HMAC-SHA256, keyed with b"vqrng-v1-extractor"
351
+ ```
352
+
353
+ ### QSeed
354
+
355
+ ```python
356
+ import vqrng
357
+
358
+ seed = vqrng.collect_seed(mode="hardware", runtime_limit=30, chsh=False, signing_key=None)
359
+ assert vqrng.verify(seed).is_valid
360
+
361
+ rng = vqrng.QSeed(seed) # PCG64 from seed["seed"]; raises vqrng.QSeedError
362
+ values = rng.integers(1, 100, size=1_000_000) # inclusive bounds; classical, replayable output
363
+ rng.generator # the numpy.random.Generator, for the full NumPy API
364
+ ```
365
+
366
+ `vqrng.QSeed(seed)` refuses a simulator seed unless `allow_simulator=True` is passed.
367
+
368
+ ### Verification
369
+
370
+ ```python
371
+ import vqrng
372
+
373
+ # Verify an evidence dictionary or JSON string
374
+ result = vqrng.verify(
375
+ evidence,
376
+ trusted_keys=["<PUBLIC_KEY_HEX>"], # optional
377
+ require_chsh=False, # True: a missing CHSH spot-check fails Level C
378
+ ibm=False, # True: also run the live IBM job check (network)
379
+ )
380
+
381
+ if result.is_valid:
382
+ print("Verification passed")
383
+ else:
384
+ print(f"Verification failed: {result.errors}")
385
+
386
+ for level in result.levels.values():
387
+ print(level.level, level.status, level.errors) # status is "pass", "fail", or "skipped"
388
+
389
+ print(result.level_b_assurance) # "checksum-only", "signed-untrusted-key", "authentic", or None if Level B failed
390
+ print(result.level_c_passed, result.chsh_s_value) # e.g. True 2.83 with chsh=True
391
+ print(result.levels.get("IBM"), result.notes) # present only with ibm=True
392
+ ```
393
+
394
+ ---
395
+
396
+ ## Verification Levels & Guarantee Hierarchy
397
+
398
+ `vqrng` structures verification into three offline tiers, plus an optional online check:
399
+
400
+ | Level | Name | Description | What It Shows |
401
+ | --- | --- | --- | --- |
402
+ | Level A | Reproducible Conversion | Replays each conditioned candidate through the rejection sampler, and checks a QSeed record's `seed` against its bytes. | The range conversion was computed correctly and without modulo bias. |
403
+ | Level B | Tamper-Evident Provenance | Recomputes the circuit and payload SHA-256 hashes, replays the health tests over the raw tape, re-conditions the tape and replays the items from it, and checks any Ed25519 signature over `pool_hash`. | Provides self-consistency checks via canonical SHA-256 hashes and optional asymmetric signature verification. Only a signature from a key you already trust shows who produced the record. |
404
+ | Level C | Near-real-time Spot-Checking Audit | Computes the CHSH value *S* from Bell-test counts recorded with `--chsh`, and checks the runs' backend and timing against the pool. | *S* > 2 physical non-locality consistency check. The reported counts violate the classical bound, and they were taken on the pool's backend within 10 seconds of it. This is not device-independent certification. The pool comes from a separate circuit, and without a trusted signature nothing proves who recorded the counts. |
405
+ | Live IBM check (`--ibm`) | IBM Job Comparison | Loads every job on the tape with your IBM token and compares status, backend, execution window, shots, CHSH counts, and submitted-circuit hashes. | The jobs this token can see still match the evidence, on a non-simulator backend. Not hardware attestation, and not available to anyone who cannot see those jobs. |
406
+
407
+ ### Limits
408
+
409
+ * **The conditioning does not create entropy.** HMAC-SHA256 is a vetted conditioning function in NIST SP 800-90B. The 512 → 256 bit ratio assumes at least 0.5 bits of min-entropy per raw bit, and `vqrng` does not estimate that. A predictable or simulated source that passes the health tests still gives random-looking, but predictable, output. The fixed public salt makes this a deterministic conditioner, not a seeded extractor in the sense of the Leftover Hash Lemma.
410
+ * **The health tests are a fault alarm.** Their cutoffs use an assumed H<sub>min</sub> = 0.5, not a measured one. There is no SP 800-90B entropy assessment and no restart test, so passing them is not NIST validation.
411
+ * **Level C does not certify the pool.** The Bell circuits run beside the Hadamard circuit that produces the numbers. The locality and detection loopholes are open. The timing check relies on the times the backend reported.
412
+ * **The live IBM check still trusts IBM.** It compares the evidence with IBM's copy of the jobs. It cannot show the qubits behaved honestly, and it fails once IBM no longer keeps a job, so long-term audit needs a trusted signature.
413
+ * **Signatures authenticate, they do not attest.** A trusted signature shows that the key holder produced the record. It says nothing about whether the key holder ran the circuits honestly.
414
+ * **QSeed output is classical.** It is PCG64, replayable by anyone holding the seed record, and not for secrets.
415
+
416
+ ---
417
+
418
+ ## Tech Stack
419
+
420
+ * **Quantum Framework:** Qiskit 1.x, `qiskit-ibm-runtime`
421
+ * **Simulation Engine:** `qiskit-aer`
422
+ * **Cryptography:** `hmac`, `hashlib` (standard library); Ed25519 verification in pure Python (RFC 8032); signing via optional `cryptography`
423
+ * **QSeed Expander:** NumPy `PCG64`
424
+ * **CLI & Packaging:** `argparse`, `setuptools`, `pyproject.toml`
425
+ * **Language:** Python 3.10+
426
+
427
+ ---
428
+
429
+ ## License
430
+
431
+ This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.