#!/usr/bin/env python3 """ Generate credentials for the managed-registry demo. Creates: creds/managed_admin.json – admin BBS+ issuer key + admin credential + issuance keypair creds/managed_registry.json – serialized registry state + cert (analyst + operator roles) creds/transport.json – server transport key (created if not already present) Users request credentials through the server at runtime (E2E-encrypted issuance). """ from __future__ import annotations import argparse import base64 import json from pathlib import Path import zkac ROLES = ("analyst", "operator") def main() -> None: ap = argparse.ArgumentParser(description="Generate managed-registry demo files.") ap.add_argument( "--output-dir", type=Path, default=Path(__file__).resolve().parent / "creds", ) args = ap.parse_args() out: Path = args.output_dir out.mkdir(parents=True, exist_ok=True) # Admin BBS+ issuer (signs credentials AND certifies registry state) admin_issuer = zkac.BbsIssuer() admin_pk = admin_issuer.public_key() admin_rid = zkac.admin_role_id() # Self-issue admin credential req = zkac.prepare_blind_request() sig = admin_issuer.issue_blind(req.commitment_with_proof(), admin_rid, 0) admin_cred = zkac.Credential.finalize( sig, req.member_secret(), req.prover_blind(), admin_rid, 0, admin_pk ) # X25519 issuance keypair for E2E-encrypted credential requests issuance_kp = zkac.IssuanceKeypair() # Build registry state with roles (admin is also the issuer for all roles) role_entries = [] for name in ROLES: role_entries.append((zkac.role_id(name), admin_pk, 1)) state = zkac.RegistryState.build( admin_pk, issuance_kp.public_key_bytes(), 1, b"\x00" * 32, role_entries ) state_bytes = state.serialize() state_cert = state.certify(admin_cred) registry_id = state.registry_id() # Save admin material admin_payload = { "admin_issuer_secret_b64": base64.b64encode(admin_issuer.secret_key_bytes()).decode(), "admin_issuer_public_key_b64": base64.b64encode(admin_pk.to_bytes()).decode(), "admin_member_secret_b64": base64.b64encode(req.member_secret()).decode(), "admin_prover_blind_b64": base64.b64encode(req.prover_blind()).decode(), "admin_blind_sig_b64": base64.b64encode(sig).decode(), "issuance_secret_b64": base64.b64encode(issuance_kp.secret_bytes()).decode(), "issuance_public_key_b64": base64.b64encode(issuance_kp.public_key_bytes()).decode(), "registry_id_hex": registry_id.hex(), } (out / "managed_admin.json").write_text(json.dumps(admin_payload, indent=2), encoding="utf-8") # Save registry state + cert reg_payload = { "registry_id_hex": registry_id.hex(), "state_bytes_b64": base64.b64encode(state_bytes).decode(), "state_cert_b64": base64.b64encode(bytes(state_cert)).decode(), "admin_issuer_public_key_b64": base64.b64encode(admin_pk.to_bytes()).decode(), "issuance_public_key_b64": base64.b64encode(issuance_kp.public_key_bytes()).decode(), "roles": list(ROLES), } (out / "managed_registry.json").write_text(json.dumps(reg_payload, indent=2), encoding="utf-8") # Transport key (create if not present) transport_path = out / "transport.json" if not transport_path.is_file(): server_kp = zkac.Keypair() transport_payload = { "server_secret_key_b64": base64.b64encode(server_kp.secret_key_bytes()).decode(), "server_public_key_b64": base64.b64encode(server_kp.public_key().to_bytes()).decode(), } transport_path.write_text(json.dumps(transport_payload, indent=2), encoding="utf-8") print(f"Wrote managed-registry demo files to {out}") print(f"Registry ID: {registry_id.hex()}") print(f"Roles: {', '.join(ROLES)}") print(f"\nAdmin can issue credentials for these roles through the server.") print(f"Users request credentials via E2E-encrypted issuance relay.") if __name__ == "__main__": main()