Doctor (qkdsec.doctor)

KME conformance probe.

The qkdsec.doctor package runs a battery of probes against a Key Management Entity (KME) and emits a conformance report against ETSI GS QKD 014 v1.1.1. It is the recommended first tool to point at any new KME, whether it is hardware, a vendor simulator, or your own implementation.

Programmatic example:

from qkdsec.client import ETSI014Client
from qkdsec.doctor import run_all, format_text

client = ETSI014Client("https://kme.example.com",
                       client_cert=("alice.crt", "alice.key"))
report = run_all(client, slave_sae_id="sae-bob")
print(format_text(report))

Or use the CLI:

qkdsec doctor https://kme.example.com --slave-sae-id sae-bob

For full options see Doctor — KME conformance probe.

class qkdsec.doctor.ProbeResult(name, status, summary, spec_section=None, severity='info', latency_ms=None, details=<factory>)[source]

Bases: object

Outcome of a single conformance probe.

Parameters:
details: dict[str, Any]
latency_ms: float | None = None
name: str
severity: str = 'info'
spec_section: str | None = None
status: ProbeStatus
summary: str
to_dict()[source]
Return type:

dict[str, Any]

class qkdsec.doctor.ProbeStatus(*values)[source]

Bases: str, Enum

FAIL = 'fail'
PASS = 'pass'
SKIP = 'skip'
WARN = 'warn'
class qkdsec.doctor.Report(base_url, slave_sae_id, results=<factory>, total_latency_ms=0.0)[source]

Bases: object

Aggregate report from a doctor run.

Parameters:
base_url: str
property counts: dict[str, int]
property passed: bool
results: list[ProbeResult]
slave_sae_id: str
to_dict()[source]
Return type:

dict[str, Any]

total_latency_ms: float = 0.0
qkdsec.doctor.format_html(report)[source]

Render the report as a standalone single-file HTML document.

Return type:

str

Parameters:

report (Report)

qkdsec.doctor.format_json(report, *, indent=2)[source]

Serialize the report as JSON. Stable field ordering for diffing.

Return type:

str

Parameters:
qkdsec.doctor.format_text(report, *, use_rich=True)[source]

Format the report as terminal text. Uses Rich if available.

Set use_rich=False to force plain ASCII output (useful for log files).

Return type:

str

Parameters:
qkdsec.doctor.run_all(client, slave_sae_id, *, consume_keys=True, latency_samples=5)[source]

Run the full conformance battery and return a Report.

Parameters:
  • client (ETSI014Client) – A configured sync client. Must have credentials for slave_sae_id.

  • slave_sae_id (str) – The slave SAE ID to probe against.

  • consume_keys (bool) – If False, skip probes that consume real keys (enc_keys / dec_keys round-trip / extensions). Default True — consumes up to 5 keys per run.

  • latency_samples (int) – Number of status calls to time for the latency probe.

Return type:

Report

Probe results

class qkdsec.doctor.ProbeResult(name, status, summary, spec_section=None, severity='info', latency_ms=None, details=<factory>)[source]

Bases: object

Outcome of a single conformance probe.

Parameters:
details: dict[str, Any]
latency_ms: float | None = None
name: str
severity: str = 'info'
spec_section: str | None = None
status: ProbeStatus
summary: str
to_dict()[source]
Return type:

dict[str, Any]

class qkdsec.doctor.ProbeStatus(*values)[source]

Bases: str, Enum

FAIL = 'fail'
PASS = 'pass'
SKIP = 'skip'
WARN = 'warn'
class qkdsec.doctor.Report(base_url, slave_sae_id, results=<factory>, total_latency_ms=0.0)[source]

Bases: object

Aggregate report from a doctor run.

Parameters:
base_url: str
property counts: dict[str, int]
property passed: bool
results: list[ProbeResult]
slave_sae_id: str
to_dict()[source]
Return type:

dict[str, Any]

total_latency_ms: float = 0.0

Probes

qkdsec.doctor.run_all(client, slave_sae_id, *, consume_keys=True, latency_samples=5)[source]

Run the full conformance battery and return a Report.

Parameters:
  • client (ETSI014Client) – A configured sync client. Must have credentials for slave_sae_id.

  • slave_sae_id (str) – The slave SAE ID to probe against.

  • consume_keys (bool) – If False, skip probes that consume real keys (enc_keys / dec_keys round-trip / extensions). Default True — consumes up to 5 keys per run.

  • latency_samples (int) – Number of status calls to time for the latency probe.

Return type:

Report

Individual conformance probes against ETSI GS QKD 014 v1.1.1.

Each probe is a pure function that returns a ProbeResult. The probes do not raise; failure modes are captured in the result so the doctor can run the full battery and report on every category, even when the KME is partially broken.

qkdsec.doctor.probes.probe_dec_keys_roundtrip(client, slave_sae_id)[source]

End-to-end round-trip: enc_keys → dec_keys returns the same bytes.

Return type:

ProbeResult

Parameters:
qkdsec.doctor.probes.probe_enc_keys_caps(client, slave_sae_id, max_per_request, max_size)[source]

Verify the KME enforces max_key_per_request and max_key_size caps.

Return type:

ProbeResult

Parameters:
qkdsec.doctor.probes.probe_enc_keys_get(client, slave_sae_id, size=256)[source]

Confirm GET enc_keys returns a valid key of the requested size.

Return type:

tuple[ProbeResult, Optional[str]]

Parameters:
qkdsec.doctor.probes.probe_enc_keys_post(client, slave_sae_id, size=256)[source]

Confirm POST enc_keys works the same as GET (ETSI 014 alternative form).

Return type:

tuple[ProbeResult, Optional[str]]

Parameters:
qkdsec.doctor.probes.probe_error_contract_400(client, slave_sae_id)[source]

Confirm the KME returns HTTP 400 for an invalid key size.

Return type:

ProbeResult

Parameters:
qkdsec.doctor.probes.probe_error_contract_404(client, slave_sae_id)[source]

Confirm the KME returns HTTP 404 for an unknown key_ID.

Return type:

ProbeResult

Parameters:
qkdsec.doctor.probes.probe_extensions_accepted(client, slave_sae_id)[source]

Check the KME does not reject optional extension fields in the body.

Return type:

ProbeResult

Parameters:
qkdsec.doctor.probes.probe_latency(client, slave_sae_id, samples=5)[source]

Measure status-endpoint latency across N samples.

Return type:

ProbeResult

Parameters:
qkdsec.doctor.probes.probe_reachability(client, slave_sae_id)[source]

Confirm the KME responds to a status request (TLS + auth working).

Return type:

ProbeResult

Parameters:
qkdsec.doctor.probes.probe_status_fields(client, slave_sae_id)[source]

Validate that the §5.2 status response includes all required fields.

Return type:

tuple[ProbeResult, Optional[dict]]

Parameters:

Formatters

qkdsec.doctor.format_text(report, *, use_rich=True)[source]

Format the report as terminal text. Uses Rich if available.

Set use_rich=False to force plain ASCII output (useful for log files).

Return type:

str

Parameters:
qkdsec.doctor.format_json(report, *, indent=2)[source]

Serialize the report as JSON. Stable field ordering for diffing.

Return type:

str

Parameters:
qkdsec.doctor.format_html(report)[source]

Render the report as a standalone single-file HTML document.

Return type:

str

Parameters:

report (Report)