Skip to content

Commit 7e6a44f

Browse files
committed
feat: seed signatures with genuine two-source quantum RNG
Replace the modal-bitstring scheme (deterministic, range [0,15]) with a two-source QRNG following the AWS Randomness_Generation reference: per-shot Hadamard bit streams from SV1 (ideal) and DM1 (noisy) condensed by a Toeplitz two-source extractor. Yields a true quantumNumber in [0,1000] plus a 32-byte fresh quantum nonce r, so signatures are non-deterministic and replay-resistant. - quantum-signature.ts: two-source circuits, toeplitzExtract, nonce-seeded ToyLWE signature; honest CSPRNG fallback (fresh, not content-derived) - webhook route: cache per-sender for stable badges; persist quantumNonce - docs (en/zh): describe the two-source + Toeplitz pipeline and genuine r - docs/paper: accuracy feedback for the paper + implementation design write-up
1 parent e294438 commit 7e6a44f

12 files changed

Lines changed: 499 additions & 206 deletions

File tree

docs/en/architecture.md

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ Telegram Photo Wall is a real-time event photo wall powered by quantum-authentic
5151
│ QUANTUM PROCESSING │ │ DATABASE │ │ STORAGE │
5252
│ MODULE │ │ LAYER │ │ LAYER │
5353
│ │ │ │ │ │
54-
│ AWS Braket SV1 │ │ DynamoDB │ │ S3 (photos) │
55-
│ ├── 4-qubit RNG circuit │ │ ├── PK/SK │ │ ├── Private, encrypted │
54+
│ AWS Braket SV1 + DM1 │ │ DynamoDB │ │ S3 (photos) │
55+
│ ├── 2-source QRNG │ │ ├── PK/SK │ │ ├── Private, encrypted │
5656
│ ├── 2-qubit Bell state │ │ │ schema │ │ ├── Pre-signed URL access │
5757
│ ├── ToyLWE signature │ │ ├── PAY_PER│ │ └── Versioned │
5858
│ └── Local crypto │ │ │ _REQUEST│ │ │
@@ -105,31 +105,32 @@ Telegram Photo Wall is a real-time event photo wall powered by quantum-authentic
105105
106106
├── [EXISTS] → Reuse existing signature (no Braket call)
107107
108-
└── [NEW SENDER] → Submit to AWS Braket SV1:
108+
└── [NEW SENDER] → Submit to AWS Braket (SV1 + DM1):
109109
110110
111-
6. DEVICE EXECUTION (AWS Braket SV1 Simulator)
112-
Task A: Quantum Random Number
113-
├── Circuit: 4-qubit (H gates → CNOT chain → Ry seed rotations → Measure)
114-
├── Shots: 100
115-
├── Output: Most frequent bitstring → integer mod 1001
111+
6. DEVICE EXECUTION (AWS Braket Simulators)
112+
Two-source QRNG (run concurrently):
113+
├── Source x: 1-qubit Hadamard on SV1 (ideal), ~700 shots → per-shot bit stream
114+
├── Source y: 1-qubit Hadamard on DM1 (noisy), ~700 shots → per-shot bit stream
116115
└── Results written to: s3://amazon-braket-*/braket-results/{taskId}/results.json
117-
118-
Task B: Bell State Measurement
116+
117+
Bell State Measurement:
119118
├── Circuit: 2-qubit (H q[0] → CNOT q[0],q[1] → Measure)
120119
├── Shots: 200
121120
└── Output: Probability distribution [P(00), P(01), P(10), P(11)]
122121
123122
124123
7. RESULT AGGREGATION
125-
├── quantumNumber = parseInt(topBitstring, 2) % 1001
124+
├── Toeplitz two-source extractor: Ext(x, y) → 288 uniform bits
125+
├── quantumNumber = readUInt32(out[0:4]) % 1001 (genuine [0, 1000])
126+
├── r = out[4:36] (32 quantum-random bytes)
126127
├── bellState = [P(00), P(01), P(10), P(11)]
127128
├── ToyLWE Signature:
128-
│ ├── Key derivation: SHAKE-256(seed + quantum_number + random_bytes)
129-
│ ├── Public key hash: SHA-256 → first 12 hex chars (uppercase)
129+
│ ├── 𝒮 = SHAKE-256(username + quantumNumber + r)
130+
│ ├── Public key hash: SHA-256(𝒮[0:32]) → first 12 hex chars (uppercase)
130131
│ └── Signature: SHA-256 chain → base64 (24 chars)
131132
├── Visual color: HSL derived from quantum number + Bell state
132-
└── Update DynamoDB: signatureStatus = "completed" + all signature fields
133+
└── Update DynamoDB: signatureStatus = "completed" + all signature fields (incl. quantumNonce)
133134
134135
135136
8. FRONTEND RENDERING
@@ -341,23 +342,24 @@ quantum:
341342

342343
| Parameter | Type | Required | Description |
343344
|-----------|------|----------|-------------|
344-
| `username` | string | Yes | Telegram sender display name (seed for circuit rotations) |
345+
| `username` | string | Yes | Telegram sender display name (signed alongside the quantum nonce) |
345346
| `messageText` | string | Yes | Message content or fallback `msg-{id}` |
346-
| Circuit type | enum | Internal | `random` (4-qubit RNG) or `bell` (2-qubit entanglement) |
347-
| Shots | number | Internal | 100 (RNG) or 200 (Bell) |
348-
| Backend | string | Config | `arn:aws:braket:::device/quantum-simulator/amazon/sv1` |
347+
| Source circuits | enum | Internal | 1-qubit Hadamard ×2 (SV1 ideal + DM1 noisy) and `bell` (2-qubit entanglement) |
348+
| Shots | number | Internal | ~700 per QRNG source, 200 (Bell) |
349+
| Backend | string | Config | `…/amazon/sv1` + `…/amazon/dm1` |
349350

350351
#### Outputs
351352

352353
| Field | Type | Description |
353354
|-------|------|-------------|
354-
| `quantumNumber` | number (0-1000) | Quantum random number from SV1 measurement |
355+
| `quantumNumber` | number (0-1000) | Quantum random number from the two-source extractor |
355356
| `publicKeyHash` | string (12 hex chars) | ToyLWE public key hash (e.g., `"7B284BB3D413"`) |
356357
| `signature` | string (24 chars, base64) | ToyLWE signature |
358+
| `nonce` | string (64 hex chars) | The 32 quantum-random bytes `r` (persisted as `quantumNonce`, audit-only) |
357359
| `bellState` | [number, number, number, number] | Bell state probabilities [P(00), P(01), P(10), P(11)] |
358-
| `algorithm` | string | `"ToyLWE-Braket-SV1"` or `"ToyLWE-local-fallback"` |
360+
| `algorithm` | string | `"ToyLWE-2Source-Toeplitz"` or `"ToyLWE-local-fallback"` |
359361
| `visualColor` | string | HSL color derived from quantum data (e.g., `"hsl(207, 85%, 55%)"`) |
360-
| `device` | string | `"SV1"` or `"local-fallback"` |
362+
| `device` | string | `"SV1+DM1"` or `"local-fallback"` |
361363

362364
#### Error Handling
363365

docs/en/quantum-key-generation.md

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,36 @@ The implementation lives at [`photo-wall/src/lib/quantum-signature.ts`](../../ph
1010

1111
### 1.1 What we generate
1212

13-
For every new sender in a Telegram group we produce a small, stable identity bundle:
13+
For every new sender in a Telegram group we produce a small identity bundle:
1414

15-
- a **quantum random number** in `[0, 1000]`, derived from a quantum measurement,
15+
- a **quantum random number** in `[0, 1000]`, harvested from quantum measurements (see §1.2),
16+
- a **32-byte quantum random nonce `r`**, the fresh entropy that seeds the signature,
1617
- a **Bell-state probability vector** `[P(00), P(01), P(10), P(11)]`, used as a structural witness,
1718
- a **ToyLWE keypair**, where the public key hash is shown on the card,
18-
- a **signature** over `username | messageText | quantumNumber`,
19-
- a deterministic **HSL accent color** derived from the quantum number and Bell-state probabilities.
19+
- a **signature** `𝒮 = SHAKE-256(username ‖ quantumNumber ‖ r)` and a derived `𝒢` (see §3.1),
20+
- an **HSL accent color** derived from the quantum number and Bell-state probabilities.
2021

21-
Subsequent messages from the same `(groupId, senderId)` reuse the cached bundle, so each user has a single stable identity per group.
22+
The bundle is seeded by fresh quantum randomness, so it is **not** reproducible from the
23+
username or message content. The first message from a `(groupId, senderId)` runs the QRNG
24+
and the bundle is stored on the row; subsequent messages **reuse the stored bundle**, so
25+
each user keeps a single stable badge per group while the underlying identity is genuinely
26+
quantum-random.
2227

2328
### 1.2 Algorithm choices and rationale
2429

2530
The pipeline is composed of three building blocks. Each is chosen for a specific reason given the constraints of an event-grade demo.
2631

2732
| Building block | Choice | Rationale |
2833
|---|---|---|
29-
| **Quantum entropy** | 4-qubit random-number circuit on Amazon Braket SV1 (100 shots), result mapped to `topBitstring mod 1001` | A small circuit fits SV1's latency budget (typical task < 5 s) and avoids QPU queue waits. Mod 1001 gives a presentation-friendly badge `Q#000``Q#1000` while still drawing entropy from quantum measurement. |
30-
| **Quantum structural witness** | 2-qubit Bell-state `\|Φ⁺⟩` circuit on SV1 (200 shots), probabilities `[P(00), P(01), P(10), P(11)]` | A perfect simulator should yield ≈ `[0.5, 0, 0, 0.5]`. Storing the empirical vector lets us drive a deterministic HSL color from quantum data and gives a visible "this came from a quantum execution" signal. |
31-
| **Post-quantum identity** | Educational ToyLWE: SHAKE-256 derives keypair material from `domain ‖ quantumSeed ‖ 32 OS bytes`; SHA-256 chain produces the signature; first 12 hex chars of the public-key digest become the badge | LWE is the same hardness assumption underlying the NIST PQC winners (Kyber/Dilithium). ToyLWE is a deliberately simple educational stand-in that keeps the on-chain artifact shape (public key, public key hash, signature) familiar to anyone planning a real PQC migration, while staying small enough to verify at a glance. |
34+
| **Quantum entropy** | Two-source QRNG: a single-qubit Hadamard circuit sampled for its **per-shot bit stream** on **SV1** (ideal source) and on **DM1** (noisy/weak source), condensed by a **Toeplitz two-source extractor** into uniform output bits | Follows the canonical AWS reference (`amazon-braket-examples``Randomness/Randomness_Generation.ipynb`). Reading every shot — rather than the modal bitstring — keeps the quantum randomness, and the two-source extractor yields output that is ε-close to uniform even under device noise. Output bytes supply both `quantumNumber ∈ [0, 1000]` and the 32-byte nonce `r`. Both circuits run on simulators, so cost stays < USD 0.01 per generation. |
35+
| **Quantum structural witness** | 2-qubit Bell-state `\|Φ⁺⟩` circuit on SV1 (200 shots), probabilities `[P(00), P(01), P(10), P(11)]` | A perfect simulator should yield ≈ `[0.5, 0, 0, 0.5]`. Storing the empirical vector lets us drive an HSL color from quantum data and gives a visible "this came from a quantum execution" signal. |
36+
| **Post-quantum identity** | Educational ToyLWE: `𝒮 = SHAKE-256(username ‖ quantumNumber ‖ r)` derives keypair material from the quantum nonce; a SHA-256 chain produces the signature; the first 12 hex chars of the public-key digest become the badge | LWE is the same hardness assumption underlying the NIST PQC winners (Kyber/Dilithium). ToyLWE is a deliberately simple educational stand-in that keeps the artifact shape (public key, public key hash, signature) familiar to anyone planning a real PQC migration, while staying small enough to verify at a glance. The full construction is specified in [`docs/paper/quantum-rng-implementation.md`](../../docs/paper/quantum-rng-implementation.md). |
3237

3338
### 1.3 What we do **not** claim
3439

3540
- **Not BB84 / E91 / QKD.** Quantum key distribution requires two cooperating endpoints with quantum hardware and a public classical channel. The wall is a single-endpoint event experience; QKD would not have been the right primitive.
3641
- **Not standardized PQC.** ToyLWE is not Kyber, Dilithium, or any NIST-standardized scheme; it is a teaching artifact. For production migration, swap ToyLWE for `@aws-crypto/kyber` / `pq-crystals/dilithium` or the equivalent in your stack — the surrounding pipeline (Braket entropy + Bell witness + per-user caching + ALB-fronted DynamoDB row) is unchanged.
37-
- **Not fault-tolerant cryptanalysis.** The 4-qubit circuit is a randomness source, not a Shor/Grover instance. The badge demonstrates "quantum-authenticated identity" at event scale, not a quantum attack or quantum-key-establishment session.
42+
- **Not fault-tolerant cryptanalysis.** The Hadamard source circuits are a randomness source, not a Shor/Grover instance. The badge demonstrates "quantum-authenticated identity" at event scale, not a quantum attack or quantum-key-establishment session.
3843

3944
---
4045

@@ -46,23 +51,24 @@ The pipeline is composed of three building blocks. Each is chosen for a specific
4651
|---|---|
4752
| Provider | Amazon Web Services |
4853
| Service | Amazon Braket |
49-
| Device | **SV1 — On-Demand State-Vector Simulator** |
50-
| Device ARN | `arn:aws:braket:::device/quantum-simulator/amazon/sv1` |
51-
| Maximum qubits | 34 (we use 4 for randomness, 2 for Bell state) |
54+
| Devices | **SV1 — On-Demand State-Vector Simulator** (ideal source) and **DM1 — Density-Matrix Simulator** (noisy/weak source) |
55+
| Device ARNs | `arn:aws:braket:::device/quantum-simulator/amazon/sv1`, `arn:aws:braket:::device/quantum-simulator/amazon/dm1` |
56+
| Qubits used | 1 per randomness source (sampled across ~700 shots each), 2 for the Bell state |
5257
| Regions used | `us-west-2` by default; configurable via `AWS_REGION_NAME` |
5358
| Result storage | S3 bucket configured by `BRAKET_BUCKET`, prefix `braket-results/` |
54-
| Typical latency | 2–5 seconds end-to-end per task |
59+
| Typical latency | 2–5 seconds end-to-end per task (run concurrently) |
5560
| Supports OpenQASM 3.0 | Yes; circuits are emitted as `braket.ir.openqasm.program` |
5661

57-
SV1 was chosen because it is queue-free, region-flexible, and its latency stays within the 5-second polling cadence of the photo wall's `GET /api/messages/[groupId]` endpoint. A real QPU run can take 5–60 minutes once queueing is included, which would force the wall into an asynchronous "pending signature" flow without a meaningful change in the demonstration's narrative.
62+
SV1 and DM1 were chosen because they are queue-free, region-flexible, and their latency stays within the 5-second polling cadence of the photo wall's `GET /api/messages/[groupId]` endpoint. Using two **independent** simulator sources — one ideal, one noisy — is what makes the Toeplitz two-source extractor meaningful: it condenses two weak sources into output that is provably close to uniform even under noise. A real QPU run can take 5–60 minutes once queueing is included, which would force the wall into an asynchronous "pending signature" flow without a meaningful change in the demonstration's narrative.
5863

5964
### 2.2 Fallback path
6065

61-
If Braket is unavailable, the code falls back to a deterministic local pipeline so the wall never blocks a sender:
66+
If Braket is unavailable, the code falls back to a local pipeline so the wall never blocks a sender. The fallback uses the OS CSPRNG (`crypto.randomBytes`) — it is **fresh and non-deterministic**, but it is **not** a quantum measurement, and it is **not** derived from message content:
6267

6368
| Stage | Fallback behavior |
6469
|---|---|
65-
| Quantum random number | `shake256(\"quantum:\" + username + \":\" + Date.now()).readUInt16BE(0) mod 1001` |
70+
| Quantum random number | `crypto.randomBytes(4).readUInt32BE(0) mod 1001` |
71+
| Nonce `r` | `crypto.randomBytes(32)` |
6672
| Bell state | Static `[0.5, 0, 0, 0.5]` (the noiseless ideal) |
6773
| Algorithm tag | `algorithm: \"ToyLWE-local-fallback\"` |
6874
| Device tag | `device: \"local-fallback\"` |
@@ -80,7 +86,7 @@ The same `BraketClient + CreateQuantumTaskCommand` path can target real QPUs wit
8086
| Neutral atom | QuEra Aquila (256 qubits, us-east-1) | Programmable layouts, AHS paradigm; not a drop-in replacement for the gate-based RNG circuit but a candidate for thematic reservoir-style outputs |
8187
| Managed simulators | DM1 (density matrix), TN1 (tensor network) | Useful when modeling noise (DM1) or wider circuits (TN1) for educational variants |
8288

83-
For an actual QPU rollout, expect to relax the 30-second polling window in `runOnSV1` and to surface a `signatureStatus = "queued"` state until the task completes.
89+
For an actual QPU rollout, expect to relax the 30-second polling window in `submitAndFetch` and to surface a `signatureStatus = "queued"` state until the task completes.
8490

8591
---
8692

@@ -93,23 +99,24 @@ export interface QuantumSignature {
9399
quantumNumber: number; // 0..1000
94100
publicKeyHash: string; // 12 uppercase hex chars
95101
signature: string; // 24 base64 chars
102+
nonce: string; // hex of the 32 quantum-random bytes r
96103
bellState: [number, number, number, number]; // [P(00), P(01), P(10), P(11)]
97-
algorithm: string; // "ToyLWE-Braket-SV1" | "ToyLWE-local-fallback"
104+
algorithm: string; // "ToyLWE-2Source-Toeplitz" | "ToyLWE-local-fallback"
98105
visualColor: string; // "hsl(h, s%, l%)"
99-
device: string; // "SV1" | "local-fallback"
106+
device: string; // "SV1+DM1" | "local-fallback"
100107
}
101108
```
102109

103110
### 3.1 Stage-by-stage contents
104111

105112
| Stage | What is produced | Where it lives in the row |
106113
|---|---|---|
107-
| **Raw quantum random bits** | Most-frequent bitstring out of 100 shots on the 4-qubit RNG circuit | Not stored verbatim; collapsed to `quantumNumber = int(topBitstring, 2) mod 1001` |
108-
| **Error-tolerant aggregation** | Picking the modal bitstring is the trivial majority-vote analogue of error correction; combined with `mod 1001`, it absorbs single-shot noise from the simulator | `quantumNumber` |
109-
| **Privacy amplification** | `xof = SHAKE-256(\"ToyLWE-KeyGen-v1\" ‖ quantumSeed ‖ os.urandom(32), 64)` mixes quantum entropy with 32 OS-random bytes, breaking any per-task correlations | Not stored; mixed into `publicKeyHash` and `signature` |
110-
| **Final key material (public artifact)** | `publicKeyHash = SHA-256(xof[0:32])[0:12]` (uppercase hex); `signature = base64(SHA-256(msgHash ‖ entropyHash ‖ pkHash))[0:24]` | `publicKeyHash`, `signature` |
114+
| **Raw quantum random bits (two sources)** | Per-shot measurement streams `x` (SV1, ideal) and `y` (DM1, noisy) from ~700 shots each of a single-qubit Hadamard circuit | Not stored verbatim |
115+
| **Two-source randomness extraction** | Toeplitz extractor `Ext(x, y) = x·(T(y)\|I_m)ᵀ mod 2` condenses the two weak sources into `m = 288` bits that are ε-close to uniform (ε = 1e-8) even under device noise | Not stored; consumed below |
116+
| **Quantum random number + nonce** | `quantumNumber = readUInt32(out[0:4]) mod 1001`; `r = out[4:36]` (the 32 fresh quantum-random bytes) | `quantumNumber`, `quantumNonce` |
117+
| **Final key material (public artifact)** | `𝒮 = SHAKE-256(username ‖ quantumNumber ‖ r, 64)`; `publicKeyHash = SHA-256(𝒮[0:32])[0:12]` (uppercase hex); `signature = base64(SHA-256(H_msg : H_ent : pkHash))[0:24]` with `H_msg = SHA-256(message)`, `H_ent = SHA-256(quantumNumber)` | `publicKeyHash`, `quantumSignature` |
111118
| **Structural witness** | Empirical Bell-state probabilities from the 2-qubit `\|Φ⁺⟩` circuit (200 shots) | `bellState` |
112-
| **Audit metadata** | Whether SV1 produced the row or the fallback did | `algorithm`, `device`, plus `signatureStatus` in DynamoDB |
119+
| **Audit metadata** | Whether the SV1+DM1 QRNG produced the row or the fallback did | `signatureAlgorithm`, `device`, plus `signatureStatus` in DynamoDB |
113120
| **Presentation derivation** | `hue = (quantumNumber × 137.5) mod 360`; `sat = 70 + bellState[0] × 30`; `light = 45 + bellState[3] × 20` | `visualColor` |
114121

115122
### 3.2 What the card actually shows
@@ -146,4 +153,4 @@ The same pipeline shape generalizes to several real workloads:
146153
- ToyLWE is for demonstration; do not use it to protect real assets.
147154
- SV1 is a simulator; the only "quantumness" being demonstrated is the entropy source and the structural witness.
148155
- The fallback path is cryptographically seeded but is **not** a quantum measurement; rely on the `device` and `algorithm` tags when communicating provenance.
149-
- The 30-second polling window in `runOnSV1` is tuned for SV1; targeting a real QPU requires extending that window and propagating a `queued` state to the UI.
156+
- The 30-second polling window in `submitAndFetch` is tuned for SV1; targeting a real QPU requires extending that window and propagating a `queued` state to the UI.

0 commit comments

Comments
 (0)