Metadata-Version: 2.4
Name: kxco-verify
Version: 1.1.0
Summary: Receiver-side verifier for the KXCO hybrid HMAC + ML-DSA-65 webhook signature scheme
Author-email: KXCO by Knightsbridge <hello@kxco.ai>
License: MIT
Project-URL: Homepage, https://kxco.ai
Project-URL: Documentation, https://chain.kxco.ai/wallet/dev-docs
Project-URL: Repository, https://github.com/JackKXCO/kxco-post-quantum-verifiers
Keywords: post-quantum,ml-dsa,dilithium,webhook,fips-204,kxco
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security :: Cryptography
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Provides-Extra: oqs
Requires-Dist: liboqs-python>=0.10; extra == "oqs"
Provides-Extra: pqcrypto
Requires-Dist: pqcrypto>=0.3; extra == "pqcrypto"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"

# kxco-verify (Python)

Receiver-side verifier for the KXCO hybrid HMAC + ML-DSA-65 webhook signature scheme. Wire-format compatible with `@kxco/post-quantum` (npm), the Go verifier, and the Rust verifier.

## Install

```bash
pip install kxco-verify           # core (HMAC, envelope, fingerprint)
pip install kxco-verify[oqs]      # adds liboqs-python for ML-DSA
pip install kxco-verify[pqcrypto] # adds pqcrypto for ML-DSA
```

The HMAC, envelope, fingerprint, and timestamp paths use only the Python standard library. ML-DSA-65 verification is lazy-loaded: it only requires a PQC backend when `verify_pq` is actually called.

## Quick start (FastAPI)

```python
from fastapi import FastAPI, Request, HTTPException
import os
import kxco_verify as kx

PINNED_KID    = "aa29f37ab7f4b2cf"  # current KXCO production kid (refresh from /.well-known if rotated)
PINNED_PUBKEY = bytes.fromhex("...3904 hex chars...")
HMAC_SECRET   = os.environ["KXCO_WEBHOOK_SECRET"].encode()

app = FastAPI()

@app.post("/webhooks/kxco")
async def webhook(request: Request):
    raw_body = await request.body()
    headers = {k.lower(): v for k, v in request.headers.items()}

    result = kx.verify_delivery(
        headers=headers,
        raw_body=raw_body,
        hmac_secret=HMAC_SECRET,
        pq_public_key=PINNED_PUBKEY,
        pinned_kid=PINNED_KID,
    )
    if not result.ok:
        raise HTTPException(401, "invalid signature")

    return {"ok": True}
```

## Running tests

```bash
cd python
python test_kxco_verify.py
```

Expected: `All 6 vector tests passed.`

This verifies that the Python implementation produces identical outputs to `vectors.json` — the same file used by the JavaScript, Go, and Rust verifiers.

## License

MIT.
