ZKAC/demo/setup_demo.py
2026-04-15 11:32:01 +02:00

80 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""
Generate demo credentials under creds/: issuer, server transport key, two member credentials.
Run once before starting the server.
"""
from __future__ import annotations
import argparse
import base64
import json
from pathlib import Path
import zkac
# Human-readable role names; each becomes a 32-byte opaque role_id via zkac.role_id().
# Must stay in sync with server.py (registry + api_body_for_role).
ROLES = ("analyst", "operator")
def main() -> None:
ap = argparse.ArgumentParser(description="Generate ZKAC demo credential files.")
ap.add_argument(
"--output-dir",
type=Path,
default=Path(__file__).resolve().parent / "creds",
help="Directory to write files (default: demo/creds)",
)
args = ap.parse_args()
out: Path = args.output_dir
out.mkdir(parents=True, exist_ok=True)
# BBS+ issuer: signs blind credentials; server only needs the public key in RoleRegistry.
issuer = zkac.BbsIssuer()
issuer_pk = issuer.public_key()
epoch = 1
# Long-term Ristretto identity for the TCP server (X25519 handshake + Schnorr identity proof).
server_kp = zkac.Keypair()
server_pk = server_kp.public_key()
issuer_payload = {
"issuer_secret_key_b64": base64.b64encode(issuer.secret_key_bytes()).decode(),
"issuer_public_key_b64": base64.b64encode(issuer_pk.to_bytes()).decode(),
}
(out / "issuer.json").write_text(json.dumps(issuer_payload, indent=2), encoding="utf-8")
transport_payload = {
"server_secret_key_b64": base64.b64encode(server_kp.secret_key_bytes()).decode(),
"server_public_key_b64": base64.b64encode(server_pk.to_bytes()).decode(),
}
(out / "transport.json").write_text(json.dumps(transport_payload, indent=2), encoding="utf-8")
# One blind issuance per role: issuer never learns member_secret.
for role_name in ROLES:
rid = zkac.role_id(role_name)
req = zkac.prepare_blind_request()
blind_sig = issuer.issue_blind(req.commitment_with_proof(), rid, epoch)
member = {
"role_name": role_name,
"role_id_hex": rid.hex(),
"epoch": epoch,
"blind_sig_b64": base64.b64encode(blind_sig).decode(),
"member_secret_b64": base64.b64encode(req.member_secret()).decode(),
"prover_blind_b64": base64.b64encode(req.prover_blind()).decode(),
"issuer_public_key_b64": base64.b64encode(issuer_pk.to_bytes()).decode(),
}
(out / f"member_{role_name}.json").write_text(
json.dumps(member, indent=2), encoding="utf-8"
)
print(f"Wrote issuer, transport, and member files to {out}")
print(
f"Roles: {', '.join(ROLES)} — use member_{ROLES[0]}.json / member_{ROLES[1]}.json with client_cli.py"
)
if __name__ == "__main__":
main()