@kirkelliott/kdfts 1.1.0 → 2.0.0

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.
package/README.md CHANGED
@@ -26,13 +26,12 @@ export ANU_API_KEY=your-key-here
26
26
  ```ts
27
27
  import { derive, formatCert } from '@kirkelliott/kdfts'
28
28
 
29
- const { key, salt, certificate } = await derive('my-password', {
29
+ const { hash, certificate } = await derive('my-password', {
30
30
  context: 'myapp:user42:session', // domain separation — optional but recommended
31
31
  })
32
32
 
33
- // Persist theseboth are needed to verify later
34
- const persistedSalt = salt.toString('hex')
35
- const persistedKey = key.toString('hex')
33
+ // Persist this single string it contains everything needed to verify later
34
+ await db.users.update({ passwordHash: hash })
36
35
 
37
36
  console.log(formatCert(certificate))
38
37
  // ════════════════════════════════════════════════════
@@ -40,8 +39,8 @@ console.log(formatCert(certificate))
40
39
  // ════════════════════════════════════════════════════
41
40
  // Source: ANU Quantum Vacuum (photon shot noise)
42
41
  // Timestamp: 2026-03-07T04:00:00.000Z
43
- // KDF: scrypt
44
- // N / r / p: 16384 / 8 / 1
42
+ // KDF: argon2id
43
+ // t / m / p: 3 / 65536 / 4
45
44
  // Key length: 32 bytes (256 bits)
46
45
  // Salt bytes: 32 bytes (256 bits)
47
46
  // Context: myapp:user42:session
@@ -56,12 +55,8 @@ console.log(formatCert(certificate))
56
55
  ```ts
57
56
  import { verify } from '@kirkelliott/kdfts'
58
57
 
59
- const salt = Buffer.from(persistedSalt, 'hex')
60
- const key = Buffer.from(persistedKey, 'hex')
61
-
62
- const valid = await verify('my-password', salt, key, {
63
- context: 'myapp:user42:session',
64
- })
58
+ const valid = await verify('my-password', storedHash)
59
+ // Parameters, salt, and context are all read from the hash string.
65
60
  ```
66
61
 
67
62
  **Reuse a source across multiple derivations** (saves one ANU round-trip):
@@ -85,15 +80,15 @@ const [keyA, keyB] = await Promise.all([
85
80
  | `context` | `string` | — | Domain separation string. Mixed in as `password \|\| 0x00 \|\| context`. |
86
81
  | `saltBytes` | `number` | `32` | Bytes of quantum entropy to fetch. |
87
82
  | `keyLength` | `number` | `32` | Output key length in bytes. |
88
- | `cost` | `{ N, r, p }` | `{ N: 16384, r: 8, p: 1 }` | scrypt parameters. Increase `N` for higher-value keys. |
83
+ | `cost` | `{ timeCost, memoryCost, parallelism }` | `{ timeCost: 3, memoryCost: 65536, parallelism: 4 }` | Argon2id parameters. `memoryCost` is in KiB. Embedded in the hash — no need to track separately. |
89
84
  | `source` | `QuantumSource` | — | Pre-created source for reuse or testing. |
90
85
 
91
86
  ## Security notes
92
87
 
93
88
  - Salt is fetched fresh for every `derive` call — never reused
94
- - Context is domain-separated with a null byte: `password || 0x00 || context`
89
+ - Context is passed as Argon2id `associatedData` and embedded in the hash `verify()` reads it automatically
95
90
  - Verification uses `crypto.timingSafeEqual` — no timing oracle on key comparison
96
- - scrypt defaults (`N=16384, r=8, p=1`) follow [OWASP recommendations](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)
91
+ - Argon2id defaults (`timeCost=3, memoryCost=65536, parallelism=4`) exceed [OWASP minimum recommendations](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)
97
92
  - The certificate records only SHA-256 hashes of the salt and key, never the raw values
98
93
  - Every claim in this README is validated by the [test suite](https://github.com/dmvjs/kdfts/actions/workflows/ci.yml)
99
94
 
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.QuantumSource = exports.verify = exports.formatCert = exports.derive = void 0;
3
+ exports.QuantumSource = exports.verify = exports.formatCert = exports.derive = exports.needsRehash = void 0;
4
+ var argon2_1 = require("argon2");
5
+ Object.defineProperty(exports, "needsRehash", { enumerable: true, get: function () { return argon2_1.needsRehash; } });
4
6
  var kdf_1 = require("./kdf");
5
7
  Object.defineProperty(exports, "derive", { enumerable: true, get: function () { return kdf_1.derive; } });
6
8
  Object.defineProperty(exports, "formatCert", { enumerable: true, get: function () { return kdf_1.formatCert; } });
package/dist/kdf.js CHANGED
@@ -4,68 +4,58 @@ exports.derive = derive;
4
4
  exports.verify = verify;
5
5
  exports.formatCert = formatCert;
6
6
  const node_crypto_1 = require("node:crypto");
7
+ const argon2_1 = require("argon2");
7
8
  const qrng_1 = require("./qrng");
8
- const scryptAsync = (password, salt, keyLen, opts) => new Promise((resolve, reject) => (0, node_crypto_1.scrypt)(password, salt, keyLen, opts, (err, key) => err ? reject(err) : resolve(key)));
9
9
  /**
10
- * Derive a cryptographic key from a password using scrypt with a
10
+ * Derive a cryptographic key from a password using Argon2id with a
11
11
  * quantum-random salt sourced from ANU vacuum fluctuations.
12
+ *
13
+ * Returns a self-describing PHC hash string. Persist it as-is.
12
14
  */
13
15
  async function derive(password, options = {}) {
14
16
  const saltBytes = options.saltBytes ?? 32;
15
17
  const keyLength = options.keyLength ?? 32;
16
18
  const context = options.context ?? null;
17
- const N = options.cost?.N ?? 16384;
18
- const r = options.cost?.r ?? 8;
19
- const p = options.cost?.p ?? 1;
20
- const qs = options.source ?? (await qrng_1.QuantumSource.create());
19
+ const timeCost = options.cost?.timeCost ?? 3;
20
+ const memoryCost = options.cost?.memoryCost ?? 65536;
21
+ const parallelism = options.cost?.parallelism ?? 4;
22
+ const qs = options.source ??
23
+ (await qrng_1.QuantumSource.create(undefined, { strict: options.strict }));
21
24
  const salt = await qs.bytes(saltBytes);
22
- const ikm = context
23
- ? Buffer.concat([
24
- Buffer.from(password),
25
- Buffer.from('\x00'),
26
- Buffer.from(context),
27
- ])
28
- : Buffer.from(password);
29
- const key = await scryptAsync(ikm, salt, keyLength, {
30
- N,
31
- r,
32
- p,
33
- maxmem: 128 * N * r * 2,
25
+ const encoded = await (0, argon2_1.hash)(Buffer.from(password), {
26
+ type: argon2_1.argon2id,
27
+ salt,
28
+ timeCost,
29
+ memoryCost,
30
+ parallelism,
31
+ hashLength: keyLength,
32
+ ...(context !== null && { associatedData: Buffer.from(context) }),
34
33
  });
34
+ // Extract hash bytes from the last segment of the PHC string for the certificate.
35
+ // Format: $argon2id$v=19$m=...,t=...,p=...[,data=...]$<salt_b64>$<hash_b64>
36
+ const parts = encoded.split('$');
37
+ const hashBytes = Buffer.from(parts[parts.length - 1], 'base64');
35
38
  const certificate = {
36
39
  source: qs.source,
37
40
  timestamp: new Date().toISOString(),
38
41
  saltHash: sha256(salt),
39
- keyHash: sha256(key),
40
- params: { N, r, p, keyLength, saltBytes },
42
+ keyHash: sha256(hashBytes),
43
+ params: { timeCost, memoryCost, parallelism, keyLength, saltBytes },
41
44
  context,
42
45
  };
43
- return { key, salt, certificate };
46
+ return { hash: encoded, certificate };
44
47
  }
45
48
  /**
46
- * Re-derive from password + salt and check against the expected key.
47
- * Uses constant-time comparison.
49
+ * Verify a password against a stored PHC hash string.
50
+ * All parameters — including context (associatedData) — are read from the hash.
48
51
  */
49
- async function verify(password, salt, key, options = {}) {
50
- const keyLength = options.keyLength ?? key.length;
51
- const N = options.cost?.N ?? 16384;
52
- const r = options.cost?.r ?? 8;
53
- const p = options.cost?.p ?? 1;
54
- const context = options.context ?? null;
55
- const ikm = context
56
- ? Buffer.concat([
57
- Buffer.from(password),
58
- Buffer.from('\x00'),
59
- Buffer.from(context),
60
- ])
61
- : Buffer.from(password);
62
- const derived = await scryptAsync(ikm, salt, keyLength, {
63
- N,
64
- r,
65
- p,
66
- maxmem: 128 * N * r * 2,
67
- });
68
- return derived.length === key.length && (0, node_crypto_1.timingSafeEqual)(derived, key);
52
+ async function verify(password, hash) {
53
+ try {
54
+ return await (0, argon2_1.verify)(hash, Buffer.from(password));
55
+ }
56
+ catch {
57
+ return false;
58
+ }
69
59
  }
70
60
  /** Format a certificate as a human-readable block. */
71
61
  function formatCert(cert) {
@@ -81,8 +71,8 @@ function formatCert(cert) {
81
71
  line,
82
72
  row('Source:', sourceLabel),
83
73
  row('Timestamp:', cert.timestamp),
84
- row('KDF:', 'scrypt'),
85
- row('N / r / p:', `${cert.params.N} / ${cert.params.r} / ${cert.params.p}`),
74
+ row('KDF:', 'argon2id'),
75
+ row('t / m / p:', `${cert.params.timeCost} / ${cert.params.memoryCost} / ${cert.params.parallelism}`),
86
76
  row('Key length:', `${cert.params.keyLength} bytes (${cert.params.keyLength * 8} bits)`),
87
77
  row('Salt bytes:', `${cert.params.saltBytes} bytes (${cert.params.saltBytes * 8} bits)`),
88
78
  cert.context ? row('Context:', cert.context) : null,
package/dist/qrng.js CHANGED
@@ -3,7 +3,8 @@
3
3
  * Quantum Random Number Generator
4
4
  *
5
5
  * Probes ANU (Australian National University) quantum vacuum fluctuations API.
6
- * Falls back silently to crypto.getRandomValues() if ANU is unavailable.
6
+ * Falls back silently to crypto.getRandomValues() if ANU is unavailable,
7
+ * unless strict mode is enabled.
7
8
  *
8
9
  * API: https://api.quantumnumbers.anu.edu.au
9
10
  * Keys: https://quantumnumbers.anu.edu.au
@@ -20,19 +21,30 @@ class QuantumSource {
20
21
  this.#source = source;
21
22
  this.#apiKey = apiKey;
22
23
  }
23
- /** Create a QuantumSource. Probes ANU first; falls back to crypto silently. */
24
- static async create(apiKey) {
24
+ /**
25
+ * Create a QuantumSource. Probes ANU first.
26
+ * Without strict mode, falls back to crypto.getRandomValues() silently if ANU is unavailable.
27
+ * With strict mode, throws if ANU is unavailable or no API key is configured.
28
+ */
29
+ static async create(apiKey, options) {
25
30
  const key = apiKey ?? process.env.ANU_API_KEY;
31
+ const strict = options?.strict ?? false;
26
32
  if (key) {
27
33
  try {
28
34
  const qs = new QuantumSource('anu', key);
29
35
  await qs.bytes(1); // probe
30
36
  return qs;
31
37
  }
32
- catch {
38
+ catch (err) {
39
+ if (strict) {
40
+ throw new Error(`ANU QRNG unavailable (strict mode): ${err instanceof Error ? err.message : String(err)}`);
41
+ }
33
42
  // fall through to crypto
34
43
  }
35
44
  }
45
+ else if (strict) {
46
+ throw new Error('ANU QRNG strict mode requires an API key — set ANU_API_KEY or pass apiKey');
47
+ }
36
48
  return new QuantumSource('crypto');
37
49
  }
38
50
  /** Which entropy source is active. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kirkelliott/kdfts",
3
- "version": "1.1.0",
4
- "description": "Quantum-seeded KDF — scrypt with salt from ANU vacuum fluctuations",
3
+ "version": "2.0.0",
4
+ "description": "Quantum-seeded KDF — Argon2id with salt from ANU vacuum fluctuations",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
@@ -27,5 +27,8 @@
27
27
  "dist",
28
28
  "README.md",
29
29
  "LICENSE"
30
- ]
30
+ ],
31
+ "dependencies": {
32
+ "argon2": "^0.44.0"
33
+ }
31
34
  }
@@ -1,232 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.runBenchmarks = runBenchmarks;
37
- /**
38
- * KwantumScrypt Boundary Tests
39
- *
40
- * These tests push the limits of the system and prove — with exact numbers —
41
- * where and why things fail. Failures are not bugs; they are fundamental.
42
- */
43
- const circuit_1 = require("../lang/circuit");
44
- const state_1 = require("../core/state");
45
- const line = '─'.repeat(60);
46
- const bold = (s) => `\x1b[1m${s}\x1b[0m`;
47
- const red = (s) => `\x1b[31m${s}\x1b[0m`;
48
- const grn = (s) => `\x1b[32m${s}\x1b[0m`;
49
- const ylw = (s) => `\x1b[33m${s}\x1b[0m`;
50
- function fmt(bytes) {
51
- if (bytes < 1024)
52
- return `${bytes} B`;
53
- if (bytes < 1024 ** 2)
54
- return `${(bytes / 1024).toFixed(1)} KB`;
55
- if (bytes < 1024 ** 3)
56
- return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
57
- return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
58
- }
59
- // ── Bell State Fidelity ────────────────────────────────────────────────────
60
- //
61
- // Prepares |Φ+⟩ = (|00⟩ + |11⟩)/√2, measures N shots.
62
- // Ideal: exactly 50% |00⟩, 50% |11⟩.
63
- // We show statistical deviation from ideal across shot counts.
64
- //
65
- async function testBellFidelity(ks) {
66
- console.log(bold('\n[1] Bell State Fidelity — (|00⟩ + |11⟩)/√2'));
67
- console.log(' Ideal: P(00) = P(11) = 0.5000');
68
- console.log(line);
69
- for (const shots of [10, 100, 1000, 10_000]) {
70
- ks.reset().h(0).cx(0, 1);
71
- const r = await ks.run(shots);
72
- const p00 = (r.counts['00'] ?? 0) / shots;
73
- const p11 = (r.counts['11'] ?? 0) / shots;
74
- const deviation = Math.abs(p00 - 0.5);
75
- const status = deviation < 0.05 ? grn('PASS') : ylw('DRIFT');
76
- console.log(` shots=${shots.toString().padStart(6)} P(00)=${p00.toFixed(4)} P(11)=${p11.toFixed(4)} Δ=${deviation.toFixed(4)} ${status}`);
77
- }
78
- }
79
- // ── GHZ State Scaling ──────────────────────────────────────────────────────
80
- //
81
- // GHZ_n = (|000...0⟩ + |111...1⟩)/√2 across n qubits.
82
- // Tests entanglement fidelity as we scale up.
83
- // Ideal: 50% all-zeros, 50% all-ones. Everything else = decoherence.
84
- //
85
- async function testGHZScaling() {
86
- console.log(bold('\n[2] GHZ State Scaling — (|0...0⟩ + |1...1⟩)/√2'));
87
- console.log(' Ideal: P(0...0) = P(1...1) = 0.5, all others = 0');
88
- console.log(line);
89
- const shots = 1000;
90
- for (const n of [2, 3, 4, 5, 6, 8, 10, 12, 16, 20]) {
91
- const ks = await circuit_1.KwantumScrypt.create(n);
92
- ks.h(0);
93
- for (let i = 1; i < n; i++)
94
- ks.cx(0, i);
95
- const r = await ks.run(shots);
96
- const allZeros = '0'.repeat(n);
97
- const allOnes = '1'.repeat(n);
98
- const p0 = (r.counts[allZeros] ?? 0) / shots;
99
- const p1 = (r.counts[allOnes] ?? 0) / shots;
100
- const leaked = 1 - p0 - p1; // probability in unwanted states
101
- const mem = fmt(new state_1.StateVector(n).memoryBytes());
102
- const status = leaked < 0.01 ? grn('IDEAL') : leaked < 0.1 ? ylw('DRIFT') : red('LEAKING');
103
- console.log(` n=${n.toString().padStart(2)} P(0s)=${p0.toFixed(3)} P(1s)=${p1.toFixed(3)} leak=${leaked.toFixed(3)} mem=${mem.padStart(8)} ${status}`);
104
- }
105
- }
106
- // ── Memory Wall ────────────────────────────────────────────────────────────
107
- //
108
- // State vector size = 2^n × 16 bytes (two float64s per complex number).
109
- // This is the fundamental reason classical quantum simulation is hard.
110
- // At ~26–28 qubits we hit RAM limits on most machines.
111
- //
112
- function testMemoryWall() {
113
- console.log(bold('\n[3] Memory Wall — State Vector Size = 2^n × 16 bytes'));
114
- console.log(' This is WHY quantum computers can\'t be classically simulated');
115
- console.log(line);
116
- for (const n of [1, 2, 4, 8, 10, 12, 16, 20, 24, 26, 28, 30, 32]) {
117
- const bytes = (1 << Math.min(n, 30)) * 2 * 8;
118
- const feasible = n <= 28;
119
- const status = n <= 20 ? grn('feasible') : n <= 26 ? ylw('slow/heavy') : red('FAILS — OOM');
120
- console.log(` n=${n.toString().padStart(2)} states=${(Math.pow(2, n)).toExponential(2).padStart(10)} RAM=${fmt(bytes).padStart(10)} ${status}`);
121
- }
122
- console.log();
123
- console.log(' ' + red('BOUNDARY: Classical simulation breaks at ~26–30 qubits.'));
124
- console.log(' ' + red('IonQ Aria (25q) and Forte (36q) operate beyond this wall.'));
125
- console.log(' ' + red('This is why real quantum hardware has value.'));
126
- }
127
- // ── Circuit Depth & Decoherence Simulation ────────────────────────────────
128
- //
129
- // In a perfect simulator, fidelity never degrades.
130
- // But we can MODEL decoherence by applying small noise after each gate.
131
- // This demonstrates what happens on real hardware (IonQ, IBM, etc.).
132
- //
133
- async function testDepthDecoherence() {
134
- console.log(bold('\n[4] Circuit Depth — Decoherence Simulation'));
135
- console.log(' Applying small depolarizing noise ε per gate layer');
136
- console.log(' Simulates T2 coherence loss on real hardware');
137
- console.log(line);
138
- const n = 3;
139
- const shots = 500;
140
- const noise = 0.01; // 1% depolarizing noise per layer
141
- for (const depth of [1, 2, 4, 8, 16, 32, 64]) {
142
- const ks = await circuit_1.KwantumScrypt.create(n);
143
- // Build a circuit of `depth` layers of H + CX
144
- ks.h(0);
145
- for (let d = 0; d < depth; d++) {
146
- for (let i = 0; i < n - 1; i++)
147
- ks.cx(i, i + 1);
148
- for (let i = 0; i < n; i++)
149
- ks.h(i);
150
- }
151
- ks.measure();
152
- const state = ks.inspect();
153
- const probs = state.probabilities();
154
- // Simulate decoherence: after `depth` layers, amplitudes decay
155
- // Real metric: entropy of the probability distribution
156
- // Higher entropy = more mixed = more decoherence
157
- const entropy = -probs.reduce((s, p) => p > 0 ? s + p * Math.log2(p) : s, 0);
158
- const maxEntropy = n; // fully mixed state has entropy = n bits
159
- const decoherence = entropy / maxEntropy;
160
- // Apply noise model: each layer depolarizes by (1-ε)^depth
161
- const noiseDecay = Math.pow(1 - noise, depth);
162
- const modelFidelity = 0.5 + 0.5 * noiseDecay; // fidelity vs ideal state
163
- const status = decoherence < 0.6 ? grn('coherent') : decoherence < 0.9 ? ylw('degrading') : red('fully mixed');
164
- console.log(` depth=${depth.toString().padStart(3)} entropy=${entropy.toFixed(3)}/${maxEntropy} model_fidelity=${modelFidelity.toFixed(3)} ${status}`);
165
- }
166
- console.log();
167
- console.log(` Noise model: ${noise * 100}% depolarizing per gate layer`);
168
- console.log(' ' + red('On real IonQ hardware: T2 ≈ 1–10ms, ~100 gates before coherence loss'));
169
- }
170
- // ── KSIF Round-trip ───────────────────────────────────────────────────────
171
- //
172
- // Encode a circuit as binary bitstrings, decode it, verify it's identical.
173
- //
174
- async function testKSIFEncoding() {
175
- console.log(bold('\n[5] KSIF Binary Encoding — Circuit ↔ Bitstring Round-trip'));
176
- console.log(' Programs expressed as quantum bitstrings');
177
- console.log(line);
178
- const { decode } = await Promise.resolve().then(() => __importStar(require('../lang/encoder')));
179
- const { CryptoQRNG } = await Promise.resolve().then(() => __importStar(require('../qrng/anu')));
180
- const ks = circuit_1.KwantumScrypt.withQRNG(3, new CryptoQRNG(), 'crypto');
181
- ks.h(0).cx(0, 1).cx(0, 2).rx(Math.PI / 4, 1).measure();
182
- const ksif = ks.toKSIF();
183
- console.log('\n Circuit encoded as KSIF bitstrings:');
184
- ksif.split('\n').forEach(line => console.log(` ${line}`));
185
- // Round-trip decode
186
- const decoded = decode(ksif);
187
- const reKs = circuit_1.KwantumScrypt.fromKSIF(ksif, 3, new CryptoQRNG(), 'crypto');
188
- const r = await reKs.run(100);
189
- console.log('\n GHZ state superposition before measurement:');
190
- Object.entries(r.superposition)
191
- .sort((a, b) => b[1] - a[1])
192
- .forEach(([bits, p]) => console.log(` |${bits}⟩ ${(p * 100).toFixed(2)}%`));
193
- console.log(`\n Decoded ${decoded.length} ops. Sample result: |${r.bitstring}⟩ ${grn('✓')}`);
194
- }
195
- // ── Quantum Notation Test ─────────────────────────────────────────────────
196
- async function testQuantumNotation() {
197
- console.log(bold('\n[6] Quantum Notation DSL — ks.bs("H|0⟩")'));
198
- console.log(line);
199
- const { CryptoQRNG } = await Promise.resolve().then(() => __importStar(require('../qrng/anu')));
200
- const ks = circuit_1.KwantumScrypt.withQRNG(3, new CryptoQRNG(), 'crypto');
201
- // Build Bell state using notation
202
- ks.bs('H|0⟩')
203
- .bs('CX|0→1⟩')
204
- .bs('CX|0→2⟩')
205
- .bs('M|*⟩');
206
- const r = await ks.run(200);
207
- console.log('\n Circuit: H|0⟩ → CX|0→1⟩ → CX|0→2⟩ → M|*⟩');
208
- console.log(' GHZ state |000⟩ + |111⟩:');
209
- Object.entries(r.counts)
210
- .sort((a, b) => b[1] - a[1])
211
- .forEach(([bits, count]) => {
212
- const bar = '█'.repeat(Math.round(count / 200 * 30));
213
- console.log(` |${bits}⟩ ${count.toString().padStart(4)} shots ${bar}`);
214
- });
215
- }
216
- // ── Main ──────────────────────────────────────────────────────────────────
217
- async function runBenchmarks() {
218
- console.log(bold('\n╔══════════════════════════════════════════════════════════╗'));
219
- console.log(bold('║ KwantumScrypt — Boundary Tests ║'));
220
- console.log(bold('╚══════════════════════════════════════════════════════════╝'));
221
- const ks2 = await circuit_1.KwantumScrypt.create(2);
222
- const source = bold(ks2['qrngSource'] === 'anu' ? grn('ANU quantum vacuum') : ylw('crypto.getRandomValues'));
223
- console.log(`\nRandom source: ${source}`);
224
- await testBellFidelity(ks2);
225
- await testGHZScaling();
226
- testMemoryWall();
227
- await testDepthDecoherence();
228
- await testKSIFEncoding();
229
- await testQuantumNotation();
230
- console.log(bold('\n' + line));
231
- console.log('Done.\n');
232
- }
@@ -1,37 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Complex = void 0;
4
- /** Immutable complex number */
5
- class Complex {
6
- re;
7
- im;
8
- constructor(re, im = 0) {
9
- this.re = re;
10
- this.im = im;
11
- }
12
- add(b) { return new Complex(this.re + b.re, this.im + b.im); }
13
- sub(b) { return new Complex(this.re - b.re, this.im - b.im); }
14
- mul(b) {
15
- return new Complex(this.re * b.re - this.im * b.im, this.re * b.im + this.im * b.re);
16
- }
17
- scale(s) { return new Complex(this.re * s, this.im * s); }
18
- conj() { return new Complex(this.re, -this.im); }
19
- abs2() { return this.re * this.re + this.im * this.im; }
20
- abs() { return Math.sqrt(this.abs2()); }
21
- toString() {
22
- if (Math.abs(this.im) < 1e-10)
23
- return this.re.toFixed(6);
24
- if (Math.abs(this.re) < 1e-10)
25
- return `${this.im.toFixed(6)}i`;
26
- const sign = this.im >= 0 ? '+' : '';
27
- return `${this.re.toFixed(6)}${sign}${this.im.toFixed(6)}i`;
28
- }
29
- static ZERO = new Complex(0, 0);
30
- static ONE = new Complex(1, 0);
31
- static I = new Complex(0, 1);
32
- /** e^(iθ) */
33
- static phase(theta) {
34
- return new Complex(Math.cos(theta), Math.sin(theta));
35
- }
36
- }
37
- exports.Complex = Complex;
@@ -1,148 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GATE_MATRICES = void 0;
4
- exports.rx = rx;
5
- exports.ry = ry;
6
- exports.rz = rz;
7
- exports.applySingleQubit = applySingleQubit;
8
- exports.applyCX = applyCX;
9
- exports.applySWAP = applySWAP;
10
- exports.applyXX = applyXX;
11
- exports.applyGPi = applyGPi;
12
- exports.applyGPi2 = applyGPi2;
13
- const complex_1 = require("./complex");
14
- const { ZERO: Z, ONE: O, I } = complex_1.Complex;
15
- /** Standard gate matrices */
16
- exports.GATE_MATRICES = {
17
- H: [[O.scale(Math.SQRT1_2), O.scale(Math.SQRT1_2)],
18
- [O.scale(Math.SQRT1_2), O.scale(-Math.SQRT1_2)]],
19
- X: [[Z, O], [O, Z]],
20
- Y: [[Z, I.scale(-1)], [I, Z]],
21
- Z: [[O, Z], [Z, O.scale(-1)]],
22
- S: [[O, Z], [Z, new complex_1.Complex(0, 1)]],
23
- SI: [[O, Z], [Z, new complex_1.Complex(0, -1)]],
24
- T: [[O, Z], [Z, complex_1.Complex.phase(Math.PI / 4)]],
25
- TI: [[O, Z], [Z, complex_1.Complex.phase(-Math.PI / 4)]],
26
- };
27
- /** Rotation gate matrices */
28
- function rx(theta) {
29
- const c = new complex_1.Complex(Math.cos(theta / 2));
30
- const s = new complex_1.Complex(0, -Math.sin(theta / 2));
31
- return [[c, s], [s, c]];
32
- }
33
- function ry(theta) {
34
- const c = new complex_1.Complex(Math.cos(theta / 2));
35
- const sp = new complex_1.Complex(Math.sin(theta / 2));
36
- const sn = new complex_1.Complex(-Math.sin(theta / 2));
37
- return [[c, sn], [sp, c]];
38
- }
39
- function rz(theta) {
40
- return [
41
- [complex_1.Complex.phase(-theta / 2), Z],
42
- [Z, complex_1.Complex.phase(theta / 2)]
43
- ];
44
- }
45
- /**
46
- * Apply a single-qubit gate to `target` qubit.
47
- * Modifies state in-place.
48
- * Complexity: O(2^n)
49
- */
50
- function applySingleQubit(state, target, mat) {
51
- const size = state.size;
52
- for (let i = 0; i < size; i++) {
53
- // Only process pairs where bit `target` = 0
54
- if ((i >> target) & 1)
55
- continue;
56
- const j = i | (1 << target);
57
- const a = state.amp[i];
58
- const b = state.amp[j];
59
- state.amp[i] = mat[0][0].mul(a).add(mat[0][1].mul(b));
60
- state.amp[j] = mat[1][0].mul(a).add(mat[1][1].mul(b));
61
- }
62
- }
63
- /**
64
- * Apply CNOT (CX) gate.
65
- * Flips `target` when `control` = 1.
66
- * Modifies state in-place.
67
- */
68
- function applyCX(state, control, target) {
69
- const size = state.size;
70
- for (let i = 0; i < size; i++) {
71
- if (!((i >> control) & 1))
72
- continue; // control must be 1
73
- if ((i >> target) & 1)
74
- continue; // target must be 0
75
- const j = i | (1 << target);
76
- const tmp = state.amp[i];
77
- state.amp[i] = state.amp[j];
78
- state.amp[j] = tmp;
79
- }
80
- }
81
- /**
82
- * Apply SWAP gate.
83
- * Modifies state in-place.
84
- */
85
- function applySWAP(state, a, b) {
86
- const size = state.size;
87
- for (let i = 0; i < size; i++) {
88
- const bitA = (i >> a) & 1;
89
- const bitB = (i >> b) & 1;
90
- if (bitA === bitB)
91
- continue;
92
- // Swap only when bits differ and bitA=0, bitB=1 (process each pair once)
93
- if (bitA !== 0)
94
- continue;
95
- const j = (i ^ (1 << a)) | (1 << b);
96
- const tmp = state.amp[i];
97
- state.amp[i] = state.amp[j];
98
- state.amp[j] = tmp;
99
- }
100
- }
101
- /**
102
- * Mølmer–Sørensen XX(θ) gate — IonQ's native 2-qubit gate.
103
- * XX(θ) = exp(-i θ X⊗X / 2)
104
- */
105
- function applyXX(state, a, b, theta) {
106
- const c = Math.cos(theta / 2);
107
- const s = Math.sin(theta / 2);
108
- const size = state.size;
109
- for (let i = 0; i < size; i++) {
110
- const ba = (i >> a) & 1;
111
- const bb = (i >> b) & 1;
112
- if (ba !== 0 || bb !== 0)
113
- continue; // process each group once
114
- const i01 = i | (1 << b);
115
- const i10 = i | (1 << a);
116
- const i11 = i | (1 << a) | (1 << b);
117
- const [v00, v01, v10, v11] = [state.amp[i], state.amp[i01], state.amp[i10], state.amp[i11]];
118
- const ni = new complex_1.Complex(0, -s);
119
- state.amp[i] = v00.scale(c).add(v11.mul(new complex_1.Complex(0, -s)));
120
- state.amp[i01] = v01.scale(c).add(v10.mul(new complex_1.Complex(0, -s)));
121
- state.amp[i10] = v10.scale(c).add(v01.mul(new complex_1.Complex(0, -s)));
122
- state.amp[i11] = v11.scale(c).add(v00.mul(new complex_1.Complex(0, -s)));
123
- void ni; // suppress lint
124
- }
125
- }
126
- /**
127
- * IonQ native GPi gate (single-qubit).
128
- * GPi(φ) = [[0, e^(-iφ)], [e^(iφ), 0]]
129
- */
130
- function applyGPi(state, target, phase) {
131
- const mat = [
132
- [complex_1.Complex.ZERO, complex_1.Complex.phase(-phase)],
133
- [complex_1.Complex.phase(phase), complex_1.Complex.ZERO]
134
- ];
135
- applySingleQubit(state, target, mat);
136
- }
137
- /**
138
- * IonQ native GPi2 gate (single-qubit).
139
- * GPi2(φ) = 1/√2 * [[1, -i·e^(-iφ)], [-i·e^(iφ), 1]]
140
- */
141
- function applyGPi2(state, target, phase) {
142
- const s = Math.SQRT1_2;
143
- const mat = [
144
- [new complex_1.Complex(s, 0), complex_1.Complex.phase(-phase).mul(new complex_1.Complex(0, -s))],
145
- [complex_1.Complex.phase(phase).mul(new complex_1.Complex(0, -s)), new complex_1.Complex(s, 0)]
146
- ];
147
- applySingleQubit(state, target, mat);
148
- }