quantumos-host-tools 0.4.0__py3-none-any.whl
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.
- qbraid_boot.py +164 -0
- qos_bridge.py +1555 -0
- qos_mcp.py +340 -0
- qpu_circuit.py +111 -0
- qsv_gateway.py +138 -0
- qsv_mirror.py +175 -0
- quantumos_host_tools-0.4.0.dist-info/METADATA +70 -0
- quantumos_host_tools-0.4.0.dist-info/RECORD +14 -0
- quantumos_host_tools-0.4.0.dist-info/WHEEL +5 -0
- quantumos_host_tools-0.4.0.dist-info/entry_points.txt +4 -0
- quantumos_host_tools-0.4.0.dist-info/licenses/LICENSE +338 -0
- quantumos_host_tools-0.4.0.dist-info/top_level.txt +8 -0
- swarm_pingpong.py +101 -0
- verify_attestation.py +98 -0
qbraid_boot.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
QuantumOS qBraid boot harness.
|
|
4
|
+
|
|
5
|
+
Draws quantum-random bytes, hands them to the QuantumOS kernel as a
|
|
6
|
+
`qseed=<hex>` boot command line, launches the kernel under QEMU, and
|
|
7
|
+
streams the serial console. On a qBraid lab machine the entropy comes
|
|
8
|
+
from a real QPU (or the free qBraid simulator); off-lab it falls back
|
|
9
|
+
to the OS CSPRNG so the harness is testable anywhere.
|
|
10
|
+
|
|
11
|
+
The kernel mixes the seed into its quantum resource manager's PRNG at
|
|
12
|
+
boot and echoes it back on serial, so a round-trip can be verified:
|
|
13
|
+
|
|
14
|
+
[BOOT] Boot entropy accepted from cmdline (qseed=)
|
|
15
|
+
[BOOT] qseed value:
|
|
16
|
+
<the same 64-bit value the harness sent>
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
python3 scripts/qbraid_boot.py # local entropy
|
|
20
|
+
python3 scripts/qbraid_boot.py --backend qbraid-sim # qBraid simulator
|
|
21
|
+
python3 scripts/qbraid_boot.py --backend <device> # real QPU on qBraid
|
|
22
|
+
python3 scripts/qbraid_boot.py --exit-on-ready # quit at "QuantumOS ready"
|
|
23
|
+
|
|
24
|
+
SPDX-License-Identifier: GPL-2.0-only
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import argparse
|
|
28
|
+
import os
|
|
29
|
+
import subprocess
|
|
30
|
+
import sys
|
|
31
|
+
|
|
32
|
+
SEED_BYTES = 8 # 64-bit qseed
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def draw_entropy_local(nbytes):
|
|
36
|
+
"""OS CSPRNG fallback (not quantum, but unblocks off-lab testing)."""
|
|
37
|
+
return os.urandom(nbytes), "local-csprng"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def draw_entropy_quantum(nbytes, backend):
|
|
41
|
+
"""Draw `nbytes` of quantum-random bytes on qBraid.
|
|
42
|
+
|
|
43
|
+
Runs a one-qubit Hadamard-and-measure circuit repeatedly; each shot
|
|
44
|
+
yields one unbiased bit. Requires the qBraid runtime (available in a
|
|
45
|
+
qBraid lab) and, for a real device, that `backend` names an
|
|
46
|
+
accessible QPU. Raises on any failure so the caller can fall back.
|
|
47
|
+
"""
|
|
48
|
+
from qbraid.runtime import QbraidProvider # qBraid lab environment
|
|
49
|
+
|
|
50
|
+
# A qBraid device id, or the free simulator by default
|
|
51
|
+
device_id = "qbraid_qir_simulator" if backend == "qbraid-sim" else backend
|
|
52
|
+
|
|
53
|
+
# One-qubit H + measure, drawn as an OpenQASM 2 program so we do not
|
|
54
|
+
# hard-depend on any single circuit SDK being importable.
|
|
55
|
+
qasm = (
|
|
56
|
+
"OPENQASM 2.0;\n"
|
|
57
|
+
'include "qelib1.inc";\n'
|
|
58
|
+
"qreg q[1];\ncreg c[1];\n"
|
|
59
|
+
"h q[0];\nmeasure q[0] -> c[0];\n"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
provider = QbraidProvider()
|
|
63
|
+
device = provider.get_device(device_id)
|
|
64
|
+
|
|
65
|
+
bits = []
|
|
66
|
+
need = nbytes * 8
|
|
67
|
+
# Batch the shots; von Neumann debiasing is unnecessary for H on an
|
|
68
|
+
# ideal simulator, and real-device bias is mixed out by the kernel's
|
|
69
|
+
# SplitMix avalanche, but we still collect raw measured bits.
|
|
70
|
+
while len(bits) < need:
|
|
71
|
+
shots = min(1024, need - len(bits))
|
|
72
|
+
job = device.run(qasm, shots=shots)
|
|
73
|
+
result = job.result()
|
|
74
|
+
counts = result.data.get_counts() if hasattr(result, "data") else result.measurement_counts()
|
|
75
|
+
# Expand counts back into a bit stream
|
|
76
|
+
for outcome, n in counts.items():
|
|
77
|
+
b = int(str(outcome)[-1])
|
|
78
|
+
bits.extend([b] * n)
|
|
79
|
+
bits = bits[:need]
|
|
80
|
+
|
|
81
|
+
out = bytearray(nbytes)
|
|
82
|
+
for i, b in enumerate(bits):
|
|
83
|
+
out[i // 8] |= (b & 1) << (i % 8)
|
|
84
|
+
return bytes(out), f"qbraid:{device_id}"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def draw_entropy(nbytes, backend):
|
|
88
|
+
if backend == "local":
|
|
89
|
+
return draw_entropy_local(nbytes)
|
|
90
|
+
try:
|
|
91
|
+
return draw_entropy_quantum(nbytes, backend)
|
|
92
|
+
except Exception as exc: # noqa: BLE001 — any failure => fall back
|
|
93
|
+
print(f"[harness] quantum entropy unavailable ({exc!r}); "
|
|
94
|
+
f"falling back to local CSPRNG", file=sys.stderr)
|
|
95
|
+
return draw_entropy_local(nbytes)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def main():
|
|
99
|
+
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
100
|
+
default_kernel = os.path.join(repo_root, "build", "x86_64", "kernel.elf32")
|
|
101
|
+
|
|
102
|
+
ap = argparse.ArgumentParser(description="QuantumOS qBraid boot harness")
|
|
103
|
+
ap.add_argument("--backend", default="local",
|
|
104
|
+
help="entropy source: 'local', 'qbraid-sim', or a QPU device id")
|
|
105
|
+
ap.add_argument("--kernel", default=default_kernel,
|
|
106
|
+
help="path to the ELF32 kernel image")
|
|
107
|
+
ap.add_argument("--qemu", default="qemu-system-x86_64")
|
|
108
|
+
ap.add_argument("--memory", default="128M")
|
|
109
|
+
ap.add_argument("--timeout", type=int, default=0,
|
|
110
|
+
help="seconds before killing QEMU (0 = run until it exits)")
|
|
111
|
+
ap.add_argument("--exit-on-ready", action="store_true",
|
|
112
|
+
help="stop once the kernel prints 'QuantumOS ready'")
|
|
113
|
+
args = ap.parse_args()
|
|
114
|
+
|
|
115
|
+
if not os.path.exists(args.kernel):
|
|
116
|
+
sys.exit(f"kernel image not found: {args.kernel}\n"
|
|
117
|
+
f"build it first: make")
|
|
118
|
+
|
|
119
|
+
seed_bytes, source = draw_entropy(SEED_BYTES, args.backend)
|
|
120
|
+
seed_hex = seed_bytes.hex()
|
|
121
|
+
# Kernel reads qseed as a big-endian hex u64 (high nibble first)
|
|
122
|
+
seed_be = int.from_bytes(seed_bytes, "big")
|
|
123
|
+
|
|
124
|
+
print(f"[harness] entropy source : {source}")
|
|
125
|
+
print(f"[harness] qseed (64-bit) : 0x{seed_be:016x}")
|
|
126
|
+
print(f"[harness] booting {os.path.basename(args.kernel)} under QEMU\n")
|
|
127
|
+
|
|
128
|
+
cmd = [
|
|
129
|
+
args.qemu,
|
|
130
|
+
"-kernel", args.kernel,
|
|
131
|
+
"-append", f"qseed={seed_hex}",
|
|
132
|
+
"-m", args.memory,
|
|
133
|
+
"-serial", "stdio",
|
|
134
|
+
"-display", "none",
|
|
135
|
+
"-no-reboot",
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
|
139
|
+
stderr=subprocess.STDOUT, text=True,
|
|
140
|
+
bufsize=1)
|
|
141
|
+
saw_seed = False
|
|
142
|
+
try:
|
|
143
|
+
for line in proc.stdout:
|
|
144
|
+
sys.stdout.write(line)
|
|
145
|
+
sys.stdout.flush()
|
|
146
|
+
if "qseed value" in line:
|
|
147
|
+
saw_seed = True
|
|
148
|
+
if args.exit_on_ready and "QuantumOS ready" in line:
|
|
149
|
+
proc.terminate()
|
|
150
|
+
break
|
|
151
|
+
except KeyboardInterrupt:
|
|
152
|
+
proc.terminate()
|
|
153
|
+
finally:
|
|
154
|
+
try:
|
|
155
|
+
proc.wait(timeout=args.timeout or None)
|
|
156
|
+
except subprocess.TimeoutExpired:
|
|
157
|
+
proc.terminate()
|
|
158
|
+
|
|
159
|
+
print(f"\n[harness] entropy handoff {'CONFIRMED' if saw_seed else 'not observed'} "
|
|
160
|
+
f"(sent 0x{seed_be:016x})")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if __name__ == "__main__":
|
|
164
|
+
main()
|