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. |
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 |
required |
host
|
str
|
The hostname (unused; dispatcher compatibility). |
required |
port
|
int
|
The port (unused; dispatcher compatibility). |
required |
require_pq_signature
|
bool
|
When |
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 |
|
PqSignatureResult
|
when either the key or the signature is post-quantum; |
|
PqSignatureResult
|
|
|
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
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |