Skip to content

PqSignature Validator

Reports the post-quantum posture of the leaf certificate, the certificate the server presents for itself. The public key algorithm and the signature algorithm are judged separately because they migrate separately: the key is the operator's choice, while the signature is applied by the issuing CA.

A hybrid composite algorithm (one identifier standing for a post-quantum and a classical algorithm used together) counts as post-quantum and is additionally flagged via is_hybrid_composite.

By default is_valid: true means the leaf key is post-quantum (the part the operator controls), matching the pq_chain default so a PQ-keyed, classically-signed certificate (one possible migration shape) gets one consistent verdict across validators. Pass require_pq_signature: true via validator args to additionally demand a post-quantum signature from the CA.

The leaf data comes from chain analysis when available, with a leaf-only fallback otherwise. If collection or parsing fails, the validator returns a failed result with a reason. Recognizing an algorithm is not cryptographic signature verification.

Opt-in

Registered but disabled by default (not in DEFAULT_VALIDATORS):

from certmonitor import CertMonitor

with CertMonitor("example.com", enabled_validators=["pq_signature"]) as m:
    print(m.validate()["pq_signature"])

# strict mode:
#   m.validate(validator_args={"pq_signature": {"require_pq_signature": True}})

How it decides

The key and the signature are judged separately; is_valid keys off the leaf key by default (the part the operator controls).

flowchart TD
    A[validate called] --> B{Leaf analysis<br/>available?}
    B -- No --> Z["is_valid: false<br/>leaf could not be analyzed"]
    B -- Yes --> C[Read leaf key algorithm<br/>and signature OID]
    C --> D{Leaf key<br/>post-quantum?}
    D -- No --> F["is_valid: false<br/>key not post-quantum"]
    D -- Yes --> E{require_pq_signature?}
    E -- "false (default)" --> G["is_valid: true"]
    E -- true --> H{CA signature<br/>post-quantum?}
    H -- Yes --> G
    H -- No --> I["is_valid: false<br/>CA signature not post-quantum"]

is_pq (reported separately from is_valid) is true when either the key or the signature is post-quantum.

Example output

A post-quantum leaf, classically signed (one possible migration shape):

These examples show selected fields from illustrative scans. validate() also adds status and code, described in the result contract.

{
    "key_algorithm": "ml-dsa-65",
    "key_is_pq": true,
    "signature_algorithm_oid": "1.2.840.113549.1.1.11",
    "signature_is_pq": false,
    "is_hybrid_composite": false,
    "is_pq": true,
    "is_valid": true
}

A classical leaf:

{
    "key_algorithm": "rsaEncryption",
    "key_is_pq": false,
    "signature_algorithm_oid": "1.2.840.113549.1.1.11",
    "signature_is_pq": false,
    "is_hybrid_composite": false,
    "is_pq": false,
    "is_valid": false,
    "reason": "Leaf key algorithm (rsaEncryption) is not post-quantum."
}

Reference

certmonitor.validators.pq_signature.PqSignatureValidator

Bases: BaseCertValidator

Report the post-quantum posture of the leaf certificate.

Judges the certificate the server presented for itself: whether its public key algorithm and its signature algorithm are post-quantum (standalone ML-DSA/SLH-DSA or a hybrid composite). The key and the signature are reported separately because they migrate separately - the key is the operator's choice, while the signature is applied by the issuing CA.

By default is_valid is True when the leaf key is post-quantum, the part the operator controls, matching the pq_chain default so a PQ-keyed, classically-signed certificate (one possible migration shape) gets one consistent verdict. Pass require_pq_signature=True to additionally demand a post-quantum signature from the CA.

The leaf data comes from chain analysis when available, with a leaf-only fallback otherwise. Collection or parsing failures produce a failed result. Algorithm recognition does not verify cryptographic signatures.

Opt-in: registered in VALIDATORS but not in DEFAULT_VALIDATORS.

Attributes:

Name Type Description
name str

The name of the validator.

name class-attribute instance-attribute

name: str = 'pq_signature'

requires class-attribute instance-attribute

requires: ClassVar = ('cert_data',)

validate

validate(cert: dict[str, Any], host: str, port: int, *, require_pq_signature: bool = False) -> PqSignatureResult

Judge the leaf certificate's post-quantum posture.

Parameters:

Name Type Description Default
cert dict[str, Any]

The cert data dict built by CertMonitor; the leaf is read from chain_analysis or the leaf_analysis fallback.

required
host str

The hostname (unused; dispatcher compatibility).

required
port int

The port (unused; dispatcher compatibility).

required
require_pq_signature bool

When True, is_valid additionally requires the CA's signature algorithm to be post-quantum. Default False: the leaf key decides.

False

Returns:

Name Type Description
dict PqSignatureResult

`{key_algorithm, key_is_pq, signature_algorithm_oid,

PqSignatureResult

signature_is_pq, is_hybrid_composite, is_pq, is_valid}` -

PqSignatureResult

per-cert field names match pq_chain. is_pq is true

PqSignatureResult

when either the key or the signature is post-quantum;

PqSignatureResult

is_hybrid_composite is true when either uses a composite

PqSignatureResult

(PQ + classical) algorithm.

Examples:

Example output (post-quantum leaf, classically signed, the realistic migration shape):

{
    "key_algorithm": "ml-dsa-65",
    "key_is_pq": true,
    "signature_algorithm_oid": "1.2.840.113549.1.1.11",
    "signature_is_pq": false,
    "is_hybrid_composite": false,
    "is_pq": true,
    "is_valid": true
}

Example output (classical leaf):

{
    "key_algorithm": "rsaEncryption",
    "key_is_pq": false,
    "signature_algorithm_oid": "1.2.840.113549.1.1.11",
    "signature_is_pq": false,
    "is_hybrid_composite": false,
    "is_pq": false,
    "is_valid": false,
    "reason": "Leaf key algorithm (rsaEncryption) is not post-quantum."
}

Source code in certmonitor/validators/pq_signature.py
def validate(
    self,
    cert: dict[str, Any],
    host: str,
    port: int,
    *,
    require_pq_signature: bool = False,
) -> PqSignatureResult:
    """Judge the leaf certificate's post-quantum posture.

    Args:
        cert: The cert data dict built by `CertMonitor`; the leaf is
            read from `chain_analysis` or the `leaf_analysis`
            fallback.
        host: The hostname (unused; dispatcher compatibility).
        port: The port (unused; dispatcher compatibility).
        require_pq_signature: When `True`, `is_valid` additionally
            requires the CA's signature algorithm to be post-quantum.
            Default `False`: the leaf key decides.

    Returns:
        dict: `{key_algorithm, key_is_pq, signature_algorithm_oid,
        signature_is_pq, is_hybrid_composite, is_pq, is_valid}` -
        per-cert field names match `pq_chain`. `is_pq` is true
        when either the key or the signature is post-quantum;
        `is_hybrid_composite` is true when either uses a composite
        (PQ + classical) algorithm.

    Examples:
        Example output (post-quantum leaf, classically signed, the
        realistic migration shape):
            ```json
            {
                "key_algorithm": "ml-dsa-65",
                "key_is_pq": true,
                "signature_algorithm_oid": "1.2.840.113549.1.1.11",
                "signature_is_pq": false,
                "is_hybrid_composite": false,
                "is_pq": true,
                "is_valid": true
            }
            ```

        Example output (classical leaf):
            ```json
            {
                "key_algorithm": "rsaEncryption",
                "key_is_pq": false,
                "signature_algorithm_oid": "1.2.840.113549.1.1.11",
                "signature_is_pq": false,
                "is_hybrid_composite": false,
                "is_pq": false,
                "is_valid": false,
                "reason": "Leaf key algorithm (rsaEncryption) is not post-quantum."
            }
            ```
    """
    leaf = self._leaf(cert)
    if leaf is None:
        return {
            "is_valid": False,
            "reason": (
                "Leaf certificate could not be analyzed: no chain or "
                "leaf analysis is available."
            ),
        }

    key_algorithm = leaf.get("public_key_info", {}).get("algorithm", "unknown")
    sig_oid = leaf.get("signature_algorithm_oid", "")
    key_is_pq = key_algorithm in _PQ_KEY_NAMES
    signature_is_pq = sig_oid in _PQ_SIG_OIDS
    is_hybrid_composite = (
        key_algorithm in _COMPOSITE_KEY_NAMES or sig_oid in _COMPOSITE_SIG_OIDS
    )

    is_valid = key_is_pq and (signature_is_pq or not require_pq_signature)

    result: PqSignatureResult = {
        "key_algorithm": key_algorithm,
        "key_is_pq": key_is_pq,
        "signature_algorithm_oid": sig_oid,
        "signature_is_pq": signature_is_pq,
        "is_hybrid_composite": is_hybrid_composite,
        "is_pq": key_is_pq or signature_is_pq,
        "is_valid": is_valid,
    }
    if not is_valid:
        if not key_is_pq:
            result["reason"] = (
                f"Leaf key algorithm ({key_algorithm}) is not post-quantum."
            )
        else:
            result["reason"] = (
                f"Leaf signature algorithm ({sig_oid}) is not post-quantum "
                "(required by require_pq_signature)."
            )
    return result