Skip to content

pqblocks-csr-pop

ML-KEM (FIPS 203) PKCS#10 Certificate Signing Request (CSR) generator with RFC 9883 Proof of Possession (PoP).

Install from GitHub Area Status Python Licence Source

At a glance

Area Interoperability
Import import pqblocks_csr_pop
Command line library only
Version 0.1.0 (Alpha)
Python >=3.11
Native dependency none
Repository PQBlocks/mlkem-csr-pop
Licence Apache-2.0

What is pqblocks-csr-pop?

ML-KEM (FIPS 203) PKCS#10 Certificate Signing Request (CSR) generator with RFC 9883 Proof of Possession (PoP).

Install

pip install git+https://github.com/PQBlocks/mlkem-csr-pop
uv add git+https://github.com/PQBlocks/mlkem-csr-pop

PyPI release staged

pqblocks-csr-pop installs from its GitHub repository while the PyPI release is staged. The import name and API are identical either way.

Quickstart

Executed, not just written

This is the quickstart from the package README. It was run against the package source when this page was generated.

quickstart.py
import datetime
from cryptography import x509
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.x509.oid import NameOID

from pqblocks_csr_pop.csr_builder import build_csr_with_pop, parse_csr, verify_csr_signature
from pqblocks_csr_pop.mlkem import mlkem_keygen

# 1. Attestor Identity (e.g. Host/Device Ed25519 key)
attestor_key = ed25519.Ed25519PrivateKey.generate()
ca_name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "Device Attestor")])
now = datetime.datetime.now(datetime.timezone.utc)
attestor_cert = (
    x509.CertificateBuilder()
    .subject_name(ca_name).issuer_name(ca_name)
    .public_key(attestor_key.public_key())
    .serial_number(x509.random_serial_number())
    .not_valid_before(now - datetime.timedelta(days=1))
    .not_valid_after(now + datetime.timedelta(days=365))
    .sign(attestor_key, None)
)

# 2. Generate ML-KEM Keypair (FIPS 203 ML-KEM-768)
ek, dk = mlkem_keygen("ML-KEM-768")

# 3. Construct PKCS#10 CSR with RFC 9883 PoP Extension
subject = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "pqc-server.internal.net")])
csr_der = build_csr_with_pop(ek, "ML-KEM-768", subject, attestor_key, attestor_cert)

# 4. CA parses CSR and verifies PoP signature
parsed = parse_csr(csr_der)
valid = verify_csr_signature(csr_der, attestor_cert)
assert valid
print(f"CSR verified successfully: {len(csr_der)} bytes DER")

Examples

Runnable scripts in the repository:

Documentation and evidence

Every PQBlocks package ships the same evidence set, so each claim can be checked against tests, sources and measurements.

  • Overview


    What it does, install, usage and API.

  • For AI agents


    Correct imports, pitfalls and rules for coding assistants.

  • Security notes


    What it protects, hostile input, constant-time and audit status.

  • Testing


    Test suites, coverage, vectors and what is not covered.

  • Benchmarks


    Measured performance, including where it loses.

  • Evidence


    Primary sources and the method-to-code map.

  • Verification report


    Self-verification results, defects found and limits.

  • Use cases


    Worked scenarios and when to pick something else.

  • Changelog


    Every release and what changed.

  • pqblocks-jwe: Compact five-part wire format, with a serializer and structural parser, for hybrid ML-KEM-768 + X25519 JWE-style…
  • pqblocks-tls-interop: TLS 1.3 hybrid key exchange (X25519MLKEM768 / RFC 10024) endpoint probe, downgrade detector, and key material validator.
  • pqblocks-wire: Length-prefixed container format (PQCWF1) for ML-KEM ciphertexts and ML-DSA / SLH-DSA signatures, with thin wrappers…