open-standards/wai/wai-quantum-py/python/wai_quantum/keys.py
Transaction Science 22fdc7a3cb wai-quantum-py: WAI Quantum in Python and notebooks, in-process
A new crate, wai/wai-quantum-py, built by maturin 1.15.0 into one abi3 wheel
per platform (pyo3 0.29.3, Python 3.9 on), module wai_quantum. Its extension
module wraps the shared job engine and nothing else: run, plan, lint, draw
(text and the engine SVG), layout, critique, improve, shortcuts,
equivalent, verify, seal, canonical, digest, methods, accuracy_classes and
history, values crossing one to one with JSON (NaN, integers past 64 bits
and non-string keys refused). A job is the service form as a plain dict
with helpers (counts, probabilities, expectations, amplitudes, method,
accuracy, receipt); Key reads and writes the command line key files and
finds the same user key; bloch gives each qubit reduced state from the
engine expectation values. A refusal raises EngineError with the engine
message, its line, and a code slot for the engine diagnostic codes.

Service is a client in the standard library alone for the job service
(submit, status, result, cancel, run), naming itself
wai-quantum-python/<version>, its receipts checked here offline.

Widgets (the widgets extra): Circuit (the engine drawing, gates clickable,
authorship marks from a history), Result, Receipt (the engine verdict) and
Bloch (drag to turn), each a plain ES module, each with static HTML (and
SVG) fallbacks that render without the widget library.

Five doc notebooks run in CI and are held to their stored outputs.
ci/check.sh is the whole check; 62 pytest tests hold the package to the
command line (same results, plans, lints, critiques, drawings, methods,
equivalence verdicts; receipts verified both ways; the same user key; the
digest recomputed from Python canonical bytes), to the service run locally,
and run the front ends under node on the payloads; Python 3.9 smoke with
the wheel alone. Workflow wai-quantum-py.yml runs it on linux and macos.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 19:51:08 -04:00

165 lines
5.3 KiB
Python

"""The key a receipt is signed with.
- ``Key.demo()``: the published demo key, for receipts that repeat byte for
byte everywhere. Anyone can sign with it, so it proves nothing about who
signed.
- ``Key.generate()``: a new key from the operating system's random source.
- ``Key.from_file(path)``, ``Key.from_hex(text)``, ``Key(seed)``: a key you
keep.
- ``Key.user()``: this user's key, the one the command line signs with:
the file ``$WAI_QUANTUM_KEY`` names, or else
``$XDG_CONFIG_HOME/wai/quantum-key`` (``~/.config/wai/quantum-key``),
made on first use and readable by its owner only.
A key file holds the 32-byte Ed25519 seed as 64 hex digits, as the command
line's does, so one key signs on both.
"""
from __future__ import annotations
import os
import secrets
from pathlib import Path
from typing import Optional, Union
from . import _engine
__all__ = ["Key", "DEMO_SEED", "key_of", "user_key_path"]
#: The published demo key's seed (the command line's ``--demo-key``).
DEMO_SEED = bytes([42] * 32)
def user_key_path() -> Optional[Path]:
"""This user's key file, as the command line finds it."""
base = os.environ.get("XDG_CONFIG_HOME")
if base:
return Path(base) / "wai" / "quantum-key"
home = os.environ.get("HOME") or str(Path.home())
return Path(home) / ".config" / "wai" / "quantum-key" if home else None
def _parse(text: str) -> Optional[bytes]:
t = text.strip()
if len(t) != 64:
return None
try:
return bytes.fromhex(t)
except ValueError:
return None
class Key:
"""An Ed25519 signing key, by its 32-byte seed."""
__slots__ = ("_seed", "path")
def __init__(self, seed: bytes, path: Optional[Path] = None) -> None:
seed = bytes(seed)
if len(seed) != 32:
raise ValueError(f"a signing key's seed is 32 bytes, not {len(seed)}")
self._seed = seed
#: The file the key was read from or made in, if any.
self.path = path
@classmethod
def demo(cls) -> "Key":
"""The published demo key: a receipt signed with it shows the result
is unaltered, not who signed it."""
return cls(DEMO_SEED)
@classmethod
def generate(cls) -> "Key":
"""A new key from the operating system's random source."""
return cls(secrets.token_bytes(32))
@classmethod
def from_hex(cls, text: str) -> "Key":
seed = _parse(text)
if seed is None:
raise ValueError("a key is 64 hex digits")
return cls(seed)
@classmethod
def from_file(cls, path: Union[str, os.PathLike]) -> "Key":
p = Path(path)
seed = _parse(p.read_text())
if seed is None:
raise ValueError(f"{p}: a key file holds 64 hex digits")
return cls(seed, p)
@classmethod
def user(cls) -> "Key":
"""This user's key, the command line's: the file ``$WAI_QUANTUM_KEY``
names (it must exist), or the user's own, made on first use."""
named = os.environ.get("WAI_QUANTUM_KEY")
if named:
return cls.from_file(named)
p = user_key_path()
if p is None:
raise ValueError("no home directory to keep a key in: pass key=")
try:
return cls.from_file(p)
except FileNotFoundError:
pass
k = cls.generate()
p.parent.mkdir(parents=True, exist_ok=True)
try:
k.save(p)
except FileExistsError:
# Made by another process meanwhile: that one is the key.
return cls.from_file(p)
k.path = p
return k
def save(self, path: Union[str, os.PathLike]) -> Path:
"""Write the key to a new file readable by its owner only; an existing
file is never overwritten."""
p = Path(path)
fd = os.open(p, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, "w") as f:
f.write(self._seed.hex() + "\n")
return p
@property
def seed(self) -> bytes:
"""The 32-byte seed: the secret itself."""
return self._seed
@property
def public_key(self) -> str:
"""The public key as 64 hex digits, as a receipt names it."""
return _engine.public_key(self._seed)
@property
def did(self) -> str:
"""The public key as a ``did:key``."""
return _engine.did_key(self.public_key)
@property
def is_demo(self) -> bool:
return self._seed == DEMO_SEED
def __eq__(self, other: object) -> bool:
return isinstance(other, Key) and other._seed == self._seed
def __hash__(self) -> int:
return hash(self.public_key)
def __repr__(self) -> str:
what = "the published demo key" if self.is_demo else (str(self.path) if self.path else "in memory")
return f"Key({self.did}, {what})"
def key_of(key: Union["Key", bytes, str, None]) -> Key:
"""A key from what a caller gives: a ``Key``, a 32-byte seed, 64 hex
digits, or ``None`` for this user's key."""
if key is None:
return Key.user()
if isinstance(key, Key):
return key
if isinstance(key, (bytes, bytearray, memoryview)):
return Key(bytes(key))
if isinstance(key, str):
return Key.from_hex(key)
raise TypeError(f"a key is a wai_quantum.Key, a 32-byte seed or 64 hex digits, not {type(key).__name__}")