betterwithage Perplexity Computer Agent commited on
Commit
b942554
·
verified ·
1 Parent(s): 6b9527b

fix(build): restore killinchu_szl_pqc_sign.py referenced by Dockerfile (unblocks build)

Browse files

The Dockerfile COPYs killinchu_szl_pqc_sign.py and serve.py imports it, but it was absent from the Space repo causing BUILD_ERROR (checksum/not found). Restoring the existing tracked file. ADDITIVE; does not alter v1/v2/v3 logic.

Signed-off-by: Yachay <yachay@szlholdings.dev>
Co-authored-by: Perplexity Computer Agent <agent@perplexity.ai>

Files changed (1) hide show
  1. killinchu_szl_pqc_sign.py +212 -0
killinchu_szl_pqc_sign.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # © 2026 Lutar, Stephen P. — SZL Holdings
3
+ # ORCID: 0009-0001-0110-4173
4
+ # Doctrine v11 — 749 declarations · 163 sorries · 14 unique axioms
5
+ """
6
+ Killinchu — additive post-quantum / hybrid DSSE signing endpoints.
7
+
8
+ Registers, ADDITIVELY (a11oy-style `register(app, ns)`):
9
+ POST /khipu/sign?mode={ecdsa,pqc,hybrid}
10
+ POST /api/killinchu/v1/khipu/sign?mode={ecdsa,pqc,hybrid}
11
+
12
+ Honest framing
13
+ --------------
14
+ * ECDSA P-256 + SHA-256 is the DEFAULT (`mode=ecdsa`). PQC is ADDITIVE.
15
+ * `mode=pqc` → ML-DSA-65 (NIST FIPS 204) only.
16
+ * `mode=hybrid` → BOTH ECDSA P-256 and ML-DSA-65; verify requires both.
17
+ Defense procurement (killinchu vertical) asks about PQC; hybrid mode live =
18
+ real competitive advantage.
19
+
20
+ ML-DSA backend resolution (graceful): liboqs via `oqs-python` (prod) →
21
+ pure-Python `dilithium-py` → if neither present, `mode=ecdsa` still works and
22
+ pqc/hybrid return HTTP 503 with an honest message (never a fake signature).
23
+
24
+ Sign: Yachay <yachay@szlholdings.dev>. Perplexity Computer Agent.
25
+ """
26
+ from __future__ import annotations
27
+
28
+ import base64
29
+ import hashlib
30
+ from typing import Optional
31
+
32
+ from cryptography.hazmat.primitives import hashes, serialization
33
+ from cryptography.hazmat.primitives.asymmetric import ec
34
+ from cryptography.exceptions import InvalidSignature
35
+
36
+ ECDSA_TYPE = "ECDSA-P256-SHA256"
37
+ MLDSA_TYPE = "ML-DSA-65"
38
+
39
+ _MLDSA_BACKEND: Optional[str] = None
40
+ _PROC_SIGNERS: dict = {}
41
+
42
+
43
+ def _detect_mldsa() -> Optional[str]:
44
+ global _MLDSA_BACKEND
45
+ if _MLDSA_BACKEND is not None:
46
+ return _MLDSA_BACKEND if _MLDSA_BACKEND != "none" else None
47
+ try:
48
+ import oqs # type: ignore
49
+ if hasattr(oqs, "Signature") and hasattr(oqs, "get_enabled_sig_mechanisms"):
50
+ _MLDSA_BACKEND = "oqs"
51
+ return "oqs"
52
+ except Exception:
53
+ pass
54
+ try:
55
+ from dilithium_py.ml_dsa import ML_DSA_65 # noqa: F401
56
+ _MLDSA_BACKEND = "dilithium_py"
57
+ return "dilithium_py"
58
+ except Exception:
59
+ pass
60
+ _MLDSA_BACKEND = "none"
61
+ return None
62
+
63
+
64
+ def _mldsa_keypair():
65
+ b = _detect_mldsa()
66
+ if b == "oqs":
67
+ import oqs # type: ignore
68
+ s = oqs.Signature(MLDSA_TYPE)
69
+ pk = s.generate_keypair()
70
+ return pk, s.export_secret_key()
71
+ if b == "dilithium_py":
72
+ from dilithium_py.ml_dsa import ML_DSA_65
73
+ return ML_DSA_65.keygen()
74
+ raise RuntimeError("no ML-DSA backend")
75
+
76
+
77
+ def _mldsa_sign(sk, msg):
78
+ b = _detect_mldsa()
79
+ if b == "oqs":
80
+ import oqs # type: ignore
81
+ with oqs.Signature(MLDSA_TYPE, sk) as s:
82
+ return s.sign(msg)
83
+ if b == "dilithium_py":
84
+ from dilithium_py.ml_dsa import ML_DSA_65
85
+ return ML_DSA_65.sign(sk, msg)
86
+ raise RuntimeError("no ML-DSA backend")
87
+
88
+
89
+ def _mldsa_verify(pk, msg, sig):
90
+ b = _detect_mldsa()
91
+ if b == "oqs":
92
+ import oqs # type: ignore
93
+ with oqs.Signature(MLDSA_TYPE) as s:
94
+ return bool(s.verify(msg, sig, pk))
95
+ if b == "dilithium_py":
96
+ from dilithium_py.ml_dsa import ML_DSA_65
97
+ return bool(ML_DSA_65.verify(pk, msg, sig))
98
+ raise RuntimeError("no ML-DSA backend")
99
+
100
+
101
+ def _pae(payload_type: str, payload: bytes) -> bytes:
102
+ t = payload_type.encode()
103
+ return b"DSSEv1 %d %s %d %s" % (len(t), t, len(payload), payload)
104
+
105
+
106
+ def _b64(d: bytes) -> str:
107
+ return base64.standard_b64encode(d).decode()
108
+
109
+
110
+ def _keyid(b: bytes) -> str:
111
+ return hashlib.sha256(b).hexdigest()[:16]
112
+
113
+
114
+ def _proc_signer(mode: str):
115
+ """Process-local signer per mode (demo/runtime; resets on restart — honest)."""
116
+ if mode in _PROC_SIGNERS:
117
+ return _PROC_SIGNERS[mode]
118
+ bundle = {"ecdsa": None, "mldsa_pk": None, "mldsa_sk": None}
119
+ if mode in ("ecdsa", "hybrid"):
120
+ bundle["ecdsa"] = ec.generate_private_key(ec.SECP256R1())
121
+ if mode in ("pqc", "hybrid"):
122
+ bundle["mldsa_pk"], bundle["mldsa_sk"] = _mldsa_keypair()
123
+ _PROC_SIGNERS[mode] = bundle
124
+ return bundle
125
+
126
+
127
+ PAYLOAD_TYPE = "application/vnd.szl.khipu+json"
128
+
129
+
130
+ def _sign(payload: bytes, mode: str) -> dict:
131
+ data = _pae(PAYLOAD_TYPE, payload)
132
+ s = _proc_signer(mode)
133
+ sigs = []
134
+ ecdsa_ok = mldsa_ok = None
135
+ if mode in ("ecdsa", "hybrid"):
136
+ sig = s["ecdsa"].sign(data, ec.ECDSA(hashes.SHA256()))
137
+ raw = s["ecdsa"].public_key().public_bytes(
138
+ serialization.Encoding.X962, serialization.PublicFormat.UncompressedPoint)
139
+ sigs.append({"keyid": _keyid(raw), "sig": _b64(sig), "sig_type": ECDSA_TYPE})
140
+ try:
141
+ s["ecdsa"].public_key().verify(sig, data, ec.ECDSA(hashes.SHA256()))
142
+ ecdsa_ok = True
143
+ except InvalidSignature:
144
+ ecdsa_ok = False
145
+ if mode in ("pqc", "hybrid"):
146
+ sig = _mldsa_sign(s["mldsa_sk"], data)
147
+ sigs.append({"keyid": _keyid(s["mldsa_pk"]), "sig": _b64(sig), "sig_type": MLDSA_TYPE})
148
+ mldsa_ok = _mldsa_verify(s["mldsa_pk"], data, sig)
149
+ if mode == "ecdsa":
150
+ verified = bool(ecdsa_ok)
151
+ elif mode == "pqc":
152
+ verified = bool(mldsa_ok)
153
+ else:
154
+ verified = bool(ecdsa_ok) and bool(mldsa_ok)
155
+ return {
156
+ "mode": mode,
157
+ "sig_types": [x["sig_type"] for x in sigs],
158
+ "verified": verified,
159
+ "doctrine": "v11",
160
+ "envelope": {
161
+ "payload": _b64(payload),
162
+ "payloadType": PAYLOAD_TYPE,
163
+ "signatures": sigs,
164
+ },
165
+ "disclosure": (
166
+ "ECDSA P-256 is the default; PQC (ML-DSA-65 / NIST FIPS 204) is "
167
+ "additive. Hybrid signs with both. Per-process keys reset on restart "
168
+ "(honest). No fake signatures: pqc/hybrid require a real ML-DSA backend."
169
+ ),
170
+ }
171
+
172
+
173
+ def register(app, ns: str = "killinchu") -> None:
174
+ from fastapi import Request
175
+ from fastapi.responses import JSONResponse
176
+
177
+ async def _handler(request: Request) -> JSONResponse:
178
+ mode = (request.query_params.get("mode") or "ecdsa").lower()
179
+ if mode not in ("ecdsa", "pqc", "hybrid"):
180
+ return JSONResponse({"error": f"unknown mode '{mode}'"}, status_code=400)
181
+ body = await request.body()
182
+ payload = body if body else b"{}"
183
+ if mode in ("pqc", "hybrid") and _detect_mldsa() is None:
184
+ return JSONResponse(
185
+ {"error": "ML-DSA backend unavailable; install 'oqs' or 'dilithium-py'. "
186
+ "ECDSA mode still available.", "mode": mode},
187
+ status_code=503,
188
+ )
189
+ try:
190
+ return JSONResponse(_sign(payload, mode))
191
+ except Exception as e: # never fake a signature
192
+ return JSONResponse({"error": str(e), "mode": mode}, status_code=503)
193
+
194
+ # Race-aware / additive: a sibling may already own POST /khipu/sign. We never
195
+ # clobber it. The namespaced PQC path is always registered; the bare path and
196
+ # an explicit /khipu/sign/pqc path are registered as available additive
197
+ # surfaces. Callers get genuine ML-DSA via the namespaced or /pqc routes
198
+ # regardless of who owns the bare path.
199
+ existing = set()
200
+ for r in getattr(app, "routes", []):
201
+ path = getattr(r, "path", None)
202
+ methods = getattr(r, "methods", None) or set()
203
+ if path and "POST" in methods:
204
+ existing.add(path)
205
+
206
+ # Always-additive PQC surfaces (guaranteed not to collide).
207
+ app.add_api_route(f"/api/{ns}/v1/khipu/sign", _handler, methods=["POST"])
208
+ app.add_api_route("/khipu/sign/pqc", _handler, methods=["POST"])
209
+
210
+ # Bare path only if free (don't clobber a sibling's existing endpoint).
211
+ if "/khipu/sign" not in existing:
212
+ app.add_api_route("/khipu/sign", _handler, methods=["POST"])