Zeek PQC Migration Observatory
zeek-pqc-migration is a Zeek community package for passive TLS
cryptographic-migration observability.
The project is designed to complement algorithm-specific PQC detection packages rather than duplicate their basic detection functionality.
Its purpose is to observe TLS cryptographic telemetry, classify cryptographic-agility signals, and produce migration intelligence about what clients appear capable of, what is negotiated, and whether a hybrid-capable client falls back to a classical key exchange.
Installation
Install the package using the Zeek Package Manager (zkg):
zkg refresh
zkg install zeek/sujalavnelavai/zeek-pqc-migration-observatory.git
Then load the package when running Zeek:
zeek zeek-pqc-migration-observatory
To analyze a packet capture:
zeek -C -r capture.pcap zeek-pqc-migration-observatory
The package writes the following logs by default:
pqc.logâ observed TLS key-exchange groups and classifications.pqc-migration.logâ migration state and fallback telemetry.pqc-tls.logâ TLS migration telemetry with connection metadata.
Using the logs
The package produces three related logs for different levels of TLS cryptographic-migration analysis.
pqc.log â observed key-exchange groups
pqc.log records TLS key-exchange groups observed during the handshake and classifies them according to the package's supported registry.
For example:
#fields ts uid id_orig_h id_orig_p id_resp_h id_resp_p group algorithm classification
... ... ... ... ... ... 29 X25519 classical
... ... ... ... ... ... 4588 X25519MLKEM768 pqc_hybrid
This log is useful when the question is: "Which key-exchange groups are clients and servers actually using?"
pqc-migration.log â migration state
pqc-migration.log combines the client's observed capability with the final negotiated group and records the resulting migration state.
For example, a hybrid negotiation can appear as:
client_capability=hybrid_capable
negotiated_group=4588
negotiated_algorithm=X25519MLKEM768
negotiated_classification=pqc_hybrid
migration_state=hybrid_negotiated
fallback=F
This indicates that the connection demonstrated hybrid capability and negotiated the validated hybrid group.
A fallback case can appear as:
client_capability=classical_and_hybrid
negotiated_group=29
negotiated_algorithm=X25519
negotiated_classification=classical
migration_state=fallback_to_classical
fallback=T
This indicates that the client demonstrated both classical and hybrid capability, but the connection ultimately negotiated the classical group.
This makes the migration log useful for identifying connections where hybrid capability exists but negotiation still results in classical cryptography.
pqc-tls.log â migration telemetry with connection context
pqc-tls.log contains the migration fields together with the connection's origin and responder addresses and ports.
This is useful when the question is: "Which connections are using which cryptographic migration state?"
For example, an analyst can use the connection metadata together with migration_state to identify the specific traffic associated with hybrid negotiations or fallback-to-classical events.
In summary:
pqc.loganswers: What key-exchange groups were observed?pqc-migration.loganswers: What cryptographic migration state did the negotiation produce?pqc-tls.loganswers: Which network connections produced those migration states?
Architecture
The project follows this processing model:
Zeek TLS telemetry
|
v
Cryptographic-agility engine
|
v
Algorithm / group registry
|
v
Migration intelligence
|
v
pqc-tls.log
The package currently focuses on TLS 1.3 key-exchange groups.
What the package observes
The implementation tracks:
- client-supported TLS key-exchange groups
- client key-share groups
- server key-share groups
- negotiated key-exchange group
- algorithm name
- cryptographic classification
- client cryptographic capability
- migration state
- fallback indication
The migration layer distinguishes between observed capability and the key exchange actually selected by the TLS session.
Unified migration output
The primary migration-observability output is:
pqc-tls.log
Each record contains:
| Field | Description |
|---|---|
ts |
Network timestamp |
uid |
Zeek connection identifier |
id_orig_h |
Originating host |
id_orig_p |
Originating port |
id_resp_h |
Responding host |
id_resp_p |
Responding port |
client_capability |
Observed client cryptographic capability |
negotiated_group |
Negotiated TLS key-exchange group |
negotiated_algorithm |
Registry algorithm name |
negotiated_classification |
classical, pqc_hybrid, or unknown |
migration_state |
Algorithm-independent migration state |
fallback |
Whether fallback to classical was identified |
For example, a validated hybrid negotiation produces:
client_capability=hybrid_capable
negotiated_group=4588
negotiated_algorithm=X25519MLKEM768
negotiated_classification=pqc_hybrid
migration_state=hybrid_negotiated
fallback=F
Migration states
The migration engine currently supports these states:
hybrid_negotiated
A PQC-hybrid group was selected and the client was observed to have hybrid capability.
fallback_to_classical
A client with observed hybrid capability negotiated a classical group.
This state is intended to provide migration intelligence rather than merely report that a classical algorithm was observed.
classical_negotiated
A classical group was negotiated and no hybrid capability was observed.
hybrid_selected_without_observed_capability
A hybrid group was selected, but the available passive telemetry did not show corresponding client hybrid capability.
handshake_failure
A TLS handshake failed after relevant client cryptographic capability was observed, but no negotiated key-exchange group could be established.
unknown_negotiation
The observed negotiation cannot be mapped to a known migration state.
Cryptographic registry
Algorithm-specific information is kept in the registry rather than embedded throughout the migration logic.
The current validated registry includes:
| Group | Algorithm | Classification |
|---|---|---|
23 |
secp256r1 |
classical |
24 |
secp384r1 |
classical |
25 |
secp521r1 |
classical |
29 |
x25519 |
classical |
30 |
x448 |
classical |
4588 |
X25519MLKEM768 |
pqc_hybrid |
The migration logic consumes the registry classification instead of hard-coding individual algorithm names into migration decisions.
This allows additional algorithms to be incorporated through the registry as the supported TLS ecosystem evolves.
Relationship to PQC detection
The project is intentionally not positioned as a replacement for algorithm-specific PQC detection.
The distinction is:
PQC detection:
"Was a PQC or hybrid algorithm observed?"
Migration observability:
"What cryptographic capability was observed,
what was negotiated,
and what does that imply about migration state?"
The existing pqc.log output remains useful as passive classification
telemetry.
The pqc-migration.log output provides the migration-oriented record.
The pqc-tls.log output provides the unified TLS migration record.
Validation
The current implementation has been validated against:
| Scenario | Group | Expected classification | Result |
|---|---|---|---|
| Classical TLS 1.3 | 29 |
classical |
PASS |
| X25519MLKEM768 TLS 1.3 | 4588 |
pqc_hybrid |
PASS |
| Unrecognized group | 12345 |
unknown |
PASS |
The migration output has additionally been validated against the X25519MLKEM768 TLS 1.3 evidence.
Expected unified record:
hybrid_capable
4588
X25519MLKEM768
pqc_hybrid
hybrid_negotiated
F
Automated tests
Run the BTest regression suite:
./testing/run-btest.sh
Expected result:
all 3 tests successful
BTEST: PASS
Run the validation suite:
./testing/run-validation.sh
Expected result:
PASS: 3
FAIL: 0
OVERALL RESULT: PASS
Evidence
Validation packet captures and classification evidence are stored under:
evidence/
The evidence includes classical TLS 1.3, PQC-hybrid TLS 1.3, and negative/unknown-group validation cases.
Scope
The current implementation focuses on passive TLS 1.3 key-exchange telemetry and migration classification.
It does not claim complete coverage of every current or future PQC, hybrid, TLS, or cryptographic-agility mechanism.
The classification registry is deliberately extensible, but individual future algorithms must still be added and validated before they can be classified.
Limitations
Passive TLS telemetry is constrained by what Zeek can observe from the TLS handshake.
The package does not infer cryptographic intent that is not present in observable protocol metadata.
A passive observer may also be unable to distinguish every possible reason for a classical negotiation. Therefore a migration state should be interpreted as evidence derived from observed TLS telemetry, not as proof of administrative policy or endpoint configuration.
Project status
The current implementation provides:
- TLS 1.3 cryptographic telemetry collection
- cryptographic-agility classification
- algorithm registry abstraction
- client capability detection
- migration-state classification
- fallback identification
- unified
pqc-tls.logoutput - BTest regression coverage
- validated classical, hybrid, and unknown-group evidence
The project is suitable for continued community review and expansion of the cryptographic registry and migration-state model.