Key takeaways:
- Poorly implemented quantum‑safe algorithms can still leak data, break interoperability, or fail under active attack.
- Consequences of potential quantum attacks can be severe, from long‑term exposure of sensitive data to system‑wide outages and silent degradation of security guarantees.
- Key goals of PQC testing include ensuring that PQC algorithms are implemented correctly and checking that they perform as intended even under heavy load (like in real-world systems).
- PQC security testing is crucial to evaluate how well your algorithms can withstand MITM attacks and other threats.
Adopting post‑quantum cryptography (PQC) is now a strategic necessity for any security‑focused product or infrastructure.
But while most organizations correctly focus on selecting and integrating NIST‑approved algorithms like ML‑KEM (Kyber), ML‑DSA (Dilithium), or SLH‑DSA, an uncomfortable truth remains:
Cryptographic migration without proper testing is more dangerous than not migrating at all.
In this article, we guide you through:
✓ Actual benefits of PQC testing
✓ Key attack vectors to keep in mind
✓ Main emulation tools to use for protection
✓ Practical testing scenarios and how to interpret results
Contents:
- Why PQC testing is a must
- Key attack vectors on PQC
- How to test PQC systems without post-quantum computers
- PQC implementation testing: Apriorit’s multilevel structure
- Verify the ML-KEM-512 implementation correctness
- Measure ML-KEM-512 performance
- Verify the ML-KEM-512 compliance
- PQC security testing using Wireshark
- Generating code for encryption/decryption
- Preparing the environment for PQC testing
- Testing scenario 1
- Testing scenario 2
- Evaluating test results
- Ensure secure and efficient PQC adoption with Apriorit
Why PQC testing is a must

PQC implementations help your product prepare for the threats of the post‑quantum world, such as the famous harvest now, decrypt later risks.
At Apriorit, we’ve seen many requests for assisting with PQC adoption. What we’ve also noticed across various projects is that even if your team integrates PQC by the book, you can’t assume your system is secure until you test how your PQC implementation behaves in real-life conditions. This is because:
1. PQC implementations are complex and highly error-prone. Real‑world PQC failures come from the wrong key encapsulation flow, encoding/decoding mismatches across components, and side‑channel vulnerabilities in software implementations. PQC algorithms are more sensitive to timing, cache access, and memory operations than classical RSA/ECC.
Only dedicated PQC testing — including negative testing, boundary cases, side-channel simulation, and multi‑platform runs — can uncover these implementation‑specific flaws.
2. PQC breaks interoperability, which can break your product. Unlike classical RSA/ECC algorithms, PQC algorithms generate larger payloads, require multi‑message exchanges, and increase handshake times and packet fragmentation. One untested handshake on a single poorly configured device can break the entire communication chain.
PQC migration requires cross‑platform, cross‑version, and hybrid interoperability testing. Otherwise, you can miss issues like Transport Layer Security (TLS) handshake failures and broken certificate chains.
3. PQC implementation might impact performance, depending on what algorithms you use and for what purpose. There’s a risk of slowing down mobile apps, on‑device encryption, embedded and automotive ECUs, and low-resource IoT devices. Skipping PQC performance testing can potentially lead to increased latency, unexpected server CPU spikes, and scalability issues on SaaS platforms.
Performance issues can’t be fully predicted based on documentation. To discover them, you need to test your PQC implementation across different devices, network conditions, and real production-like workloads. It’s better to confirm zero performance trade-offs during testing than to receive negative feedback after release.
4. Systems that adopt PQC aren’t automatically crypto‑agile. Crypto agility is the ability to upgrade cryptography without rebuilding the system. To ensure your team has actually achieved it, you must check, say, that replacing RSA with Kyber won’t cause system-wide outages. Or make sure that misaligned key formats won’t silently corrupt data.
5. Rushed PQC implementation may require costly re-engineering later. Migrating to PQC is a multi‑year journey involving extra work with protocol stacks, firmware, cloud services, client applications, identity systems, and key management infrastructure. Diligent PQC testing helps you discover and fix issues before deployment.
6. PQC implementations are still maturing. Even widely used implementations may contain incomplete algorithm support or integration issues with existing protocols (TLS, SSH, VPNs). PQC tests are therefore necessary to ensure correct algorithm implementation, compatibility with existing cryptographic stacks, and safe fallback mechanisms when hybrid cryptography is used.
Watch webinar
Post-Quantum Cryptography: Practical Readiness and Secure Integration
Learn how to assess your organization’s readiness for post-quantum threats and avoid costly security gaps. Explore practical approaches to secure integration of next-generation cryptographic solutions.
What to look for during PQC testing: key attack vectors
Before your team can meaningfully test PQC deployments, it’s crucial to clearly understand the threats your project might face.
Common PQC threats to consider when developing a testing strategy
| Attack vector | Why it’s dangerous | Consequences |
|---|---|---|
| MITM & hybrid‑mode attacks | If hybrid negotiation is flawed, the attacker may force a fallback to a vulnerable classical path. | – Silent interception – Malicious code injection – Traffic manipulation |
| Implementation weaknesses | Bugs in cryptographic libraries, incorrect parameter handling, or insecure randomness generation can introduce vulnerabilities. | – Decryption failures – Key leakage – Signature forgery |
| Side‑channel & timing attacks | Non‑constant time implementations or cache‑sensitive operations can leak key‑dependent information. | – Recovery of private keys – Forging signatures – Decrypting protected communications |
| Replay & traffic‑reproduction attacks | If message freshness or session logic isn’t enforced, attackers may cause unintended state transitions. | – Repeated execution of sensitive operations – Session desynchronization – Potential denial of service |
How to test PQC systems without post-quantum computers: Welcome post‑quantum emulators
A challenge that researchers and security engineers now face is how to test cryptographic systems against quantum‑level threats when real large‑scale quantum computers don’t yet exist.
This is where post‑quantum emulators become essential: they simulate the cryptographic conditions under which classical algorithms become vulnerable and only quantum‑resistant schemes remain reliable.
A PQC emulator provides software‑based implementations of quantum‑safe mechanisms while:
- Supporting key generation, encapsulation, decapsulation, and digital signatures
- Using algorithms specifically designed to resist quantum attacks
- Allowing engineers to test PQC in authentic system environments: network protocols, encrypted traffic, client–server interactions, file encryption workflows, and more
- Enabling teams to test their systems as if the quantum era has already arrived
- Allowing organizations to proactively strengthen their infrastructure before quantum attacks become practical
Here are the main tools and platforms for post‑quantum emulation as of today:
- Open Quantum Safe (OQS) is one of the leading projects in the field. Its liboqs library implements a wide range of post‑quantum algorithms including Kyber, Dilithium, and SPHINCS+. OQS is commonly used for testing key encapsulation, signatures, and full cryptographic flows within real software stacks.
- OQS‑OpenSSL is an extension of the classic OpenSSL library that integrates PQC algorithms. This allows practical testing of quantum‑safe TLS connections, hybrid key exchanges, and PQC‑enhanced certificates using familiar tooling.
- PQClean is a project offering clean, auditable reference implementations of PQC algorithms. It’s designed for correctness review, performance comparison, and reproducibility. PQClean is often used as the baseline implementation when verifying or benchmarking other libraries.
- Large vendors like IBM and Microsoft have begun integrating PQC into their platforms, signing mechanisms, and cloud services. IBM provides PQC‑based algorithm suites built around CRYSTALS‑Kyber and CRYSTALS‑Dilithium, while Microsoft Research contributes to PQC tooling for system‑level testing and migration planning.
- Virtualization and containerized environments like QEMU and Docker now allow engineers to set up isolated testing environments that simulate entire systems (clients, servers, adversarial nodes) and analyze encrypted traffic in detail.
To help you and your team better understand how this may work in practice, we have prepared several practical examples on how we test:
- Correctness, performance, and compliance (FIPS 203 standard) of PQC implementation
- Security of post-quantum cryptography implementations
Or you can reach out and we’ll discuss effective ways to assess and enhance the protection of your product’s PQC layer.
Concerned about vulnerabilities in post-quantum algorithms?
Leverage Apriorit’s security testing expertise to evaluate your system against evolving threats and cybersecurity skills and advance your product’s defences.
PQC implementation testing: Apriorit’s multilevel structure
In cryptography, an implementation mistake may result in failed secure connections or hidden vulnerabilities. This is why we recommend taking a multi-level approach and starting with assessing your PQC algorithm implementation before you move to security testing.
For our example, let’s take the ML-KEM-512 algorithm. ML-KEM belongs to the class of Key Encapsulation Mechanisms (KEM). The purpose of a KEM is to allow two parties to agree on a shared secret over an open communication channel.
For a program under testing, let’s say we have a simple PQC-driven solution that runs the following steps:
- Key generation. The receiver generates a key pair: a public key (can be shared openly) and a private key (must remain secret).
- Encapsulation. Using the receiver’s public key, the sender generates a ciphertext and derives a shared secret (shared_secret_A). This step is typically executed by the sender during connection establishment.
- Decapsulation. The receiver uses their private key and the received ciphertext to derive the corresponding shared secret (shared_secret_B).
As this section offers a simplified, illustrative example of how to run PQC testing, we’ll only check for three metrics:
- Correctness 一 To make sure the algorithm is implemented correctly and works as intended
- Performance 一 To validate whether the algorithm is suitable for real-world deployments
- Compliance 一 To check whether the algorithm is implemented according to the FIPS 203 standard
With that in mind, let’s start testing the algorithm implementation.
Verify ML-KEM-512 implementation correctness using liboqs
Our task here is to:
- Verify the correctness of the ML-KEM-512 implementation standardized by NIST (FIPS 203) using the liboqs library
- Confirm that encapsulation and decapsulation operations produce the same shared secret on both sides
The implementation is considered correct if the following condition holds: shared_secret_A == shared_secret_B.
1. Prepare an isolated environment using a clean Docker container:
docker run -it --name pqc-lab ubuntu:22.04 bashAfter execution, the new Ubuntu 22.04 environment starts up and the following message appears indicating that the session is running inside the container: root@<container_id>:/#,
2. Install dependencies. Since the container initially contains some system packages, we have to update the package list:
apt updateNow, we can install the required tools:
apt install -y build-essential cmake ninja-build git libssl-devNote: If libssl-dev is not installed, the build process will fail due to missing OpenSSL components.
3. Obtain the source code:
git clone https://github.com/open-quantum-safe/liboqs.git
cd liboqs4. Create a separate build directory:
mkdir build
cd buildNow, we can configure the project:
cmake -GNinja -DOQS_BUILD_ONLY_LIB=OFF ..If the configuration is successful, the output will include:
-- Configuring done
-- Generating doneNow we can compile the project: ninja
5. Run the ML-KEM-512 test:
./tests/test_kem ML-KEM-512Finally, here’s our output:
Testing KEM algorithms using liboqs version 0.15.0 #Displays the library version. Configuration info
==================
Target platform: aarch64-Linux-6.10.14-linuxkit #Beginning of environment and build configuration details
Compiler: gcc (11.4.0). #Shows the CPU architecture and operating system where the test is running
Compile options: [-march=armv8-a+crypto;-Wa,--noexecstack;-O3;-fomit-frame-pointer;-fdata-sections;-ffunction-sections;-Wl,--gc-sections;-Wbad-function-cast] #Indicates which compiler and version were used to build the library
OQS version: 0.15.0 (major: 0, minor: 15, patch: 0) #Displays optimization and security-related compilation flags applied during build
Git commit: f1e80d17b452ec71a01b68ac7cbb52649f0b7544 #Confirms the exact version of liboqs being tested
OpenSSL enabled: Yes (OpenSSL 3.0.2 15 Mar 2022) #Specifies the exact source code revision used, ensuring reproducibility
#Confirms that OpenSSL is integrated and shows its version
AES: OpenSSL
SHA-2: OpenSSL
SHA-3: C
OQS build flags: OQS_DIST_BUILD OQS_LIBJADE_BUILD OQS_OPT_TARGET=generic CMAKE_BUILD_TYPE=Release #Indicates which backend implementation is used for cryptographic primitives
CPU exts active: AES SHA2 SHA3 NEON #Shows compilation configuration flags for the OQS build
================================================================================
#Lists active hardware acceleration extensions available on the CPU
sample computation for KEM ML-KEM-512
Version source: FIPS203
================================================================================
#Indicates that a test computation is being performed for ML-KEM-512, following the FIPS 203 specification
shared secrets are equal
shared secrets are equalBoth lines confirm that the shared secrets computed by the sender and receiver match.
Let’s check the return code: echo $?
A return value of 0 indicates successful execution.
This verification:
- Demonstrates that the ML-KEM-512 implementation behaves exactly as defined in the FIPS 203 specification
- Confirms that key pair generation operates correctly, meaning that public and private keys are produced in a valid and internally consistent manner
- Verifies that the encapsulation procedure correctly derives a ciphertext and an initial shared secret on the sender’s side
- Confirms that the decapsulation procedure on the receiver’s side reconstructs the identical shared secret from the ciphertext using the corresponding private key
Practical significance: If this verification were to fail, it would indicate a fundamental implementation problem. In practical systems, such failure would prevent successful key agreement between communicating parties.
Any protocol relying on ML-KEM for secure session establishment — including TLS-based systems, VPN tunnels, or encrypted messaging frameworks — would either fail during handshake or produce unstable connections.
After verifying that ML-KEM-512 functions correctly and produces identical shared secrets on both sides, the next logical step is to evaluate its computational performance.
Related project
Auditing the Security of a Connected Vehicle Communication System
Discover how we identified critical vulnerabilities in a vehicle communication system and helped enhance its overall security posture through a comprehensive audit and targeted remediation.

Measure ML-KEM-512 performance
Our objectives here are to:
- Find out whether the algorithm is suitable for real-world deployment under load
- Measure the execution time of key generation, encapsulation, and decapsulation
- Assess whether ML-KEM-512 can be used in production environments such as TLS handshakes, VPN systems, and high-load servers without introducing unacceptable latency
From a performance perspective, encapsulation and decapsulation are particularly important because they occur during every secure session establishment. If these operations are slow, overall handshake latency increases accordingly. Performance benchmarking provides quantitative data about how long each operation takes and how many operations can be performed per second.
Alexander, Test Engineer at Apriorit
1. Prepare the environment. We’ll use the same Docker container as in the previous section to ensure consistency of the environment, including:
- CPU architecture
- Compiler version
- Optimization flags
- OpenSSL configuration
- liboqs version
Now, we can:
- Move to the build directory:
cd /liboqs/build - Verify that benchmarking tools are available:
ls tests - Check that output includes the binary:
speed_kem
2. Launch the built-in benchmarking tool for the ML-KEM-512 algorithm:
./tests/speed_kem ML-KEM-512The output begins with configuration information describing the execution environment:
Configuration info
==================
Target platform: aarch64-Linux-6.10.14-linuxkit
Compiler: gcc (11.4.0)
Compile options: [-march=armv8-a+crypto;-Wa,--noexecstack;-O3;-fomit-frame-pointer;-fdata-sections;-ffunction-sections;-Wl,--gc-sections;-Wbad-function-cast]
OQS version: 0.15.0 (major: 0, minor: 15, patch: 0)
Git commit: 97f6b86b1b6d109cfd43cf276ae39c2e776aed80
OpenSSL enabled: Yes (OpenSSL 3.0.2 15 Mar 2022)
AES: OpenSSL
SHA-2: OpenSSL
SHA-3: C
OQS build flags: BUILD_SHARED_LIBS OQS_DIST_BUILD OQS_LIBJADE_BUILD OQS_OPT_TARGET=generic CMAKE_BUILD_TYPE=Release
CPU exts active: AES SHA2 SHA3 NEON
Speed test
==========This section confirms:
- ARM64 architecture (aarch64)
- GCC compiler version
- Aggressive optimization (-O3)
- Hardware cryptographic acceleration (AES, SHA, NEON)
3. Interpret the speed test results:
Speed test
==========
Started at 2026-04-05 18:20:24
Operation | Iterations | Total time (s) | Time (us): mean | pop. stdev | High-prec time (ns): mean | pop. stdev
------------------------------------ | ----------:| --------------:| ---------------:| ----------:| -------------------------:| ----------:
ML-KEM-512 | | | | | |
keygen | 494462 | 3.000 | 6.067 | 11.735 | 6036 | 11732
encaps | 448017 | 3.000 | 6.696 | 1.550 | 6665 | 1478
decaps | 400699 | 3.000 | 7.487 | 10.937 | 7456 | 10927- Each operation is executed repeatedly for a fixed duration of three seconds.
- The program measures how many iterations can be performed within that time and calculates the average execution time.
- Key generation was performed 484,367 times in three seconds, resulting in an average time of approximately 6.2 microseconds per operation. This corresponds to roughly 160,000 key generations per second.
- Encapsulation averaged approximately 6.8 microseconds. This operation is typically performed by the client in a TLS handshake.
- Decapsulation averaged approximately 8.2 microseconds. It is slightly slower due to additional internal computations.
We can conclude that all operations complete within single-digit microseconds.
Practical interpretation: One millisecond equals 1,000 microseconds. Typical internet latency ranges from several milliseconds to tens of milliseconds. This means ML-KEM operations execute approximately one thousand times faster than typical network delays.
What this verification confirms:
- ML-KEM-512 has high computational efficiency on the tested ARM64 architecture with hardware acceleration enabled.
- Our algorithm maintains stable timing characteristics.
- The computational cost of ML-KEM-512 doesn’t significantly affect overall connection establishment time.
These results indicate that ML-KEM-512 is suitable for deployment in real-world systems, including TLS-based secure communication and high-throughput server environments.
Read also
How to Integrate Post-Quantum Cryptography Algorithms into Your Software: A Practical Guide
Find out how to effectively integrate post-quantum cryptography into your applications and infrastructure. Discover proven approaches that help you strengthen security, reduce risks, and ensure readiness for evolving threats.

Verify ML-KEM-512 compliance using known answer tests (KAT)
Under normal operation, ML-KEM uses cryptographically secure randomness. For KAT, this randomness is replaced with a fixed seed. The algorithm receives a strictly defined input and must produce a strictly defined output.
Effectively, ML-KEM temporarily behaves as a deterministic function: seed → pk, sk, ct, ss
For this test, our goals are to:
- Confirm that the ML-KEM-512 implementation reproduces exactly the same values defined by the FIPS 203 standard
- Check correctness of polynomial ring arithmetic
- Ensure proper serialization and encoding
- Check correctness of hashing procedures
If even a single byte differs from the reference value, the implementation is considered non-compliant.
1. Prepare an environment using the same Docker container as for previous tests and taking the same steps.
2. Run KAT and analyze the output:
./tests/kat_kem ML-KEM-512The output begins as follows:
count = 0
seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1
pk = 400865ED10B619AA5811139BC086825782B2B7124F757C83AE794444BC78A47896ACF1262C81351077893BFC56F90449C2FA5F6E586DD37C0B9B581992638CB7E7BCBBB99AFE4781D80A50E69463FBD988722C3635423E27466C71DCC674527CCD728968CBCDC00C5C9035BB0AF2C9922C7881A41DD2875273925131230F6CA59E9136B39F956C93B3B2D14C641B089E07D0A840C893ECD76BBF92C805456668D07C621491C5C054991A656F511619556EB97782E27A3C785124C70B0DABA6C624D18E0F9793F96BA9E1599B17B30DCCC0B4F3766A07B23B257309CD76ABA072C2B9C9744394C6AB9CB6C54A97B5C57861A58DC0A03519832EE32A07654A070C0C8C4E8648ADDC355F274FC6B92A087B3F9751923E44274F858C49CABA72B65851B3ADC48936955097CAD9553F5A263F1844B52A020FF7CA89E881A01B95D957A3153C0A5E0A1CCD66B1821A2B8632546E24C7CBBC4CB08808CAC37F7DA6B16F8ACED052CDB2564948F1AB0F768A0D3286CCC7C3749C63C781530FA1AE670542855004A645B522881EC1412BDAE342085A9DD5F8126AF96BBDB0C1AF69A15562CB2A155A100309D1B641D08B2D4ED17BFBF0BC04265F9B10C108F850309504D772811BBA8E2BE16249AA737D879FC7FB255EE7A6A0A753BD93741C61658EC074F6E002B019345769113CC013FF7494BA8378B11A172260AAA53421BDE03A35589D57E322FEFA4100A4743926AB7D62258B87B31CCBB5E6B89CB10B271AA05D994BB5708B23AB327ECB93C0F3156869F0883DA2064F795E0E2AB7D3C64D61D2303FC3A29E1619923CA801E59FD752CA6E7649D303C9D20788E1214651B06995EB260C929A1344A849B25CA0A01F1EB52913686BBA619E23714464031A78439287FCA78F4C0476223EEA61B7F25A7CE42CCA901B2AEA129817894BA3470823854F3E5B28D86BA979E54671862D90470B1E7838972A81A48107D6AC0611406B21FBCCE1DB7702EA9DD6BA6E40527B9DC663F3C93BAD056DC28511F66C3E0B928DB8879D22C592685CC775A6CD574AC3BCE3B27591C821929076358A2200B377365F7EFB9E40C3BF0FF0432986AE4BC1A242CE9921AA9E22448819585DEA308EB039
sk = 9CDA1686A3396A7C109B415289F56A9EC44CD5B9B674C38A3BBAB30A2C90F00437A264B0BE9A1E8BA887D3C3B100898054272F941C88A1F208F1C914F964C1AAD613A6A84F88E42D3556835FB161FDC5CD15A3BC7E74B6F2612FA8271C7EA112B05C2A36CC707CE38D5D1ACC5115462A8C1AABF07276C72318337F74B5CBEFEA7A803790BC0393F3A54C724A5765A48F296B03F484376023626930222704C08FD3BC729315D1FC70EB7975A97B9DEED162F486BBC64A097111952D89B57D765E8A991A2E564206EA7BF5E4007A66358831CA0E34B2F6A84D10F79C477CB66A8A952569367388130D7B974A63AA51996C97709BB8EABC94E6A535D792D2905474952D6B8C2222B2AE56DC66FB0461192066CDDB43EC05984FB4982649771397C6A8379F3B5643069848875919E89CC439A3BE2F081490F341BD1240ADD80DDB8C9963B47A2A0992290338DA9C3B725C6DA44718C01046812562AFB084837ACB3C575E4F93936C352AC0E70AA3845EE485296E6B02DE0B47B5C4C96B0B7CF94C4ABE95486153118E43C2B9C84D9DA91C6C5ACD5A57002D058497992799E5BA1CE6C25EB29844D858BA1C37850C0C2F57C60DE37F77C082EC14494EBA288A65915116C20A325DE31AAADD680DB19C0CFCC3460F0AA01A87A6A580C6CA291FAEF0CCC49B76A8DAC4F9D41640509DBD0B4045C1530ED34755D47462700F2A8CAF9680A6D7E38A7E2A63E937650A23306D855DA2A2B7EF505CA596AB0485013EA927C7342343613643BA4007D6C874B980C79C3AA1C74F8581C34849B36EA79815FBB4CCF9610583081D7C5B4409B8D0531C04BCAF7CC751103A5FD1BA4470833E89775ADED970B5471859250FE7267105835F390030C5E7CD3F961019EAAEA23777D347BB2ADCB673C02034F394342271BCEA6414E546C3B20BD57481C7EA14C77C388CC86251C12558B100F8C5B3D03CA2C70713909659C8BA26D0D1765E0BC823D68CA5570DE600CD0941725D386E14C1012DF5951BEB8D8281A4F6815D3760B764295AD0406C2BF7928AD65032B65F14B77CCB8917C93A29D6287D8A6062399CB6400865ED10B619AA5811139BC086825782B2B7124F757C83AE794444BC78A47896ACF1262C81351077893BFC56F90449C2FA5F6E586DD37C0B9B581992638CB7E7BCBBB99AFE4781D80A50E69463FBD988722C3635423E27466C71DCC674527CCD728968CBCDC00C5C9035BB0AF2C9922C7881A41DD2875273925131230F6CA59E9136B39F956C93B3B2D14C641B089E07D0A840C893ECD76BBF92C805456668D07C621491C5C054991A656F511619556EB97782E27A3C785124C70B0DABA6C624D18E0F9793F96BA9E1599B17B30DCCC0B4F3766A07B23B257309CD76ABA072C2B9C9744394C6AB9CB6C54A97B5C57861A58DC0A03519832EE32A07654A070C0C8C4E8648ADDC355F274FC6B92A087B3F9751923E44274F858C49CABA72B65851B3ADC48936955097CAD9553F5A263F1844B52A020FF7CA89E881A01B95D957A3153C0A5E0A1CCD66B1821A2B8632546E24C7CBBC4CB08808CAC37F7DA6B16F8ACED052CDB2564948F1AB0F768A0D3286CCC7C3749C63C781530FA1AE670542855004A645B522881EC1412BDAE342085A9DD5F8126AF96BBDB0C1AF69A15562CB2A155A100309D1B641D08B2D4ED17BFBF0BC04265F9B10C108F850309504D772811BBA8E2BE16249AA737D879FC7FB255EE7A6A0A753BD93741C61658EC074F6E002B019345769113CC013FF7494BA8378B11A172260AAA53421BDE03A35589D57E322FEFA4100A4743926AB7D62258B87B31CCBB5E6B89CB10B271AA05D994BB5708B23AB327ECB93C0F3156869F0883DA2064F795E0E2AB7D3C64D61D2303FC3A29E1619923CA801E59FD752CA6E7649D303C9D20788E1214651B06995EB260C929A1344A849B25CA0A01F1EB52913686BBA619E23714464031A78439287FCA78F4C0476223EEA61B7F25A7CE42CCA901B2AEA129817894BA3470823854F3E5B28D86BA979E54671862D90470B1E7838972A81A48107D6AC0611406B21FBCCE1DB7702EA9DD6BA6E40527B9DC663F3C93BAD056DC28511F66C3E0B928DB8879D22C592685CC775A6CD574AC3BCE3B27591C821929076358A2200B377365F7EFB9E40C3BF0FF0432986AE4BC1A242CE9921AA9E22448819585DEA308EB03950C8DD152A4531AAB560D2FC7CA9A40AD8AF25AD1DD08C6D79AFE4DD4D1EEE5AB505D7CFAD1B497499323C8686325E4792F267AAFA3F87CA60D01CB54F29202A
ct = 521C88486C35F6C245839212AB0E23660CD5B68FCCD5A7B41EB5A3CE8844A31088C878EEFEB44739CF9130013A83FAAA78037443E5D749BA4D6F156934CC89C2D9ABC76CB7FF050B4EEEB4A58611BE330B3FDEE875C1F366216AD659FABBEBCE37114E795C65F1EECA93181343005410FEBAE042DFAEEAD873CF1C575D38CE26EC5C02940C0224E983881C2A1A4771BA316628A0F425EF54E984FE70E3866C79780B7572462CE5A9E116B55439AE921FF8B0D89D8616D405135DFAB8F14D7DA03F752517DA847458AB83646CE5B4073788C66A6B60FAF64B8FED507EE2A7D931F746B9F2595769721A59D93E4852AAF8185114F4A04F0F6F3CA144BA8EE1BA52DB4AA7DC274156862812DC36E06997942BAB02822BFC5FDFCDACEA869C1A7672A4C794C9C09CC8A76DF894324C14A53E9961CF40F0E70DC18583AA5E3D025A5B8D9CEDA71D7902EBC5D499F059386B9910C75BA834B9D0C70AD9B9EA683AA699865F9CA7F3F30D20B78FF99850216A62F919A9D9ECA482A52EAA2500FE5B80853CBB88E17CE593EB23709BAC01FDFC941B527F5180E0DECC3785F04D9120098F14C07F9244B441F2897F243C846A1D093D6A9C0B40E842A6D12E1D2E01BB44693D61C875EF007673787AAF167C1EC2B2F61AB8B504032A14490C109A0C2AEE872FCD629594992EBD6DCDE42FF6A602A5C7E15F50B799A7780829DB1CB2E70E89944CF543224D4339CCF317A0BA195A07DF0F43D7EEE2400080DA25A40F320061B15AE23EA0DEE42474B2274D92C72C7E82F938BF826934CA2AAACA49CD73EB36D182591B8145D89AC8D6CEB7BE8A1D7960D04171D7D03D84580BCA9B5976AD1ED6CC8B021BEECDBCC8B51A9B091C6625861097A32FB5A41E15B856CDA135C3CA29C8656603CE3EB78071494197F0906D8B2A2CB208076EC89CE5760B199E937E13FEBC7893665AB6B2D5C85DC9A5D873CBF55B4A69343D768FBEEF4B5EB88D0C31FFD366C66E13866E3F33EECBF2C3329C111C0CDE2B9560892CE1A2686A2A1C18B7A7261A55BDA57ADE241544F3561390BDC69514429C8D5FBEA9188BAF2892
ss = B4C8E3C4115F9511F2FDDB288C4B78C5CD7C89D2D4D321F46B4EDC54DDF0EB36Let’s break down what the received output means:
line count = 0indicates the test vector number.seedis the fixed deterministic input used for generation.pkis the public key derived strictly from the provided seed.skis the corresponding private key.ctis the encapsulation result.ssis the shared secret computed during the test.
Each of these values matches the official reference vectors defined by the standard.
What this verification confirms:
- ML-KEM-512 implementation behaves deterministically when supplied with a fixed seed.
- Every value produced during the test — public key, private key, ciphertext, and derived shared secret — is fully determined by the initial seed value.
- Our algorithm is implemented precisely according to the FIPS 203 specification, as every byte of every output matches the expected reference output.
With correctness confirmed, performance measured, and deterministic conformance validated, the ML-KEM-512 implementation has passed the foundational layers of cryptographic testing. These stages collectively establish confidence in both the functional and structural integrity of the algorithm before it’s integrated into higher-level protocols such as TLS or hybrid cryptographic deployments.
Read also
Preparing Your Software for Post-Quantum Cryptography: A Practical Guide to Crypto-Agility
Learn how to build crypto agility into your systems and adapt quickly to emerging security standards. Strengthen your approach to post-quantum readiness with practical implementation insights.

PQC security testing using Wireshark
Let’s shift from checking algorithm correctness to testing security.
First, we need to generate encryption/decryption code as our base for testing and prepare an environment for PQC testing. Then, to test post-quantum cryptography algorithms, we’ll run two scenarios, showing you how PQC testing looks in practice.
Generating code for encryption/decryption
Using AI, we created the encryption and decryption code in Python.
Encryption code:
import oqs
import os
def encrypt_file(input_file, public_key_file, encrypted_file, ciphertext_file):
kem = oqs.KeyEncapsulation("Kyber512")
with open(public_key_file, "rb") as f:
public_key = f.read()
with open(input_file, "rb") as f:
plaintext = f.read()
ciphertext, shared_secret = kem.encap_secret(public_key)
# Extend shared_secret to cover the entire plaintext
extended_secret = (shared_secret * (len(plaintext) // len(shared_secret) + 1))[:len(plaintext)]
# Encrypt the entire file
encrypted_data = bytes(a ^ b for a, b in zip(plaintext, extended_secret))
with open(encrypted_file, "wb") as f:
f.write(encrypted_data)
with open(ciphertext_file, "wb") as f:
f.write(ciphertext)
print(f"File encrypted successfully! Encrypted file: {encrypted_file}, Ciphertext: {ciphertext_file}")
def generate_keys(public_key_file, private_key_file):
kem = oqs.KeyEncapsulation("Kyber512")
public_key = kem.generate_keypair()
private_key = kem.export_secret_key()
with open(public_key_file, "wb") as f:
f.write(public_key)
with open(private_key_file, "wb") as f:
f.write(private_key)
print(f"Keys generated successfully! Public key: {public_key_file}, Private key: {private_key_file}")
if __name__ == "__main__":
input_file = "pqc.txt"
public_key_file = "public_key.bin"
private_key_file = "private_key.bin"
encrypted_file = "pqc_encrypted.bin"
ciphertext_file = "ct.bin"
generate_keys(public_key_file, private_key_file)
encrypt_file(input_file, public_key_file, encrypted_file, ciphertext_file)
print("Send the files pqc_encrypted.bin and ct.bin to your friend.")This is how it works:
- We run the following code:
python3 encrypt_pqc.py. - Our program creates a pair of keys (public and private) using Kyber512:
public_key.binandprivate_key.bin. - The program takes
public_key.binandpqc.txtas inputs. - The program creates a shared secret and a ciphertext:
ct.bin. - The program encrypts the content of
pqc.txtwith the secret key (here we used simple XOR for illustrative purposes; in real-life solutions, it should be AES). - The result is saved as
pqc_encrypted.bin(the encrypted file) andct.bin(the ciphertext).
If we open the pqc_encrypted.bin file, we’ll see the following:
×?—/»±†ï?Llè¥4·_ë-îñö ÒÆG>After executing the program, the following output is produced:
python3 encrypt_pqc.py
The keys are created! Public: public_key.bin, Private: private_key.bin
The file is encrypted! The encrypted file: pqc_encrypted.bin, the ciphertext: ct.bin
Send files to a friend: pqc_encrypted.bin and ct.binNow, let’s look at the decryption code:
import oqs
import os
def decrypt_file(encrypted_file, ciphertext_file, private_key_file, output_file):
kem = oqs.KeyEncapsulation("Kyber512")
with open(private_key_file, "rb") as f:
private_key = f.read()
with open(ciphertext_file, "rb") as f:
ciphertext = f.read()
kem = oqs.KeyEncapsulation("Kyber512", private_key)
shared_secret = kem.decap_secret(ciphertext)
with open(encrypted_file, "rb") as f:
encrypted_data = f.read()
# Extend shared_secret to cover the entire encrypted data
extended_secret = (shared_secret * (len(encrypted_data) // len(shared_secret) + 1))[:len(encrypted_data)]
# Decrypt all data
decrypted_data = bytes(a ^ b for a, b in zip(encrypted_data, extended_secret))
with open(output_file, "wb") as f:
f.write(decrypted_data)
print(f"File decrypted successfully! Output: {output_file}")
if __name__ == "__main__":
encrypted_file = "pqc_encrypted.bin"
ciphertext_file = "ct.bin"
private_key_file = "private_key.bin"
output_file = "pqc_decrypted.txt"
for file in [encrypted_file, ciphertext_file, private_key_file]:
if not os.path.exists(file):
print(f"Error: file {file} was not found!")
exit()
decrypt_file(encrypted_file, ciphertext_file, private_key_file, output_file)
with open(output_file, "r", encoding="utf-8") as f:
print("Decrypted file contents:", f.read())Let’s run python3 decrypt_pqc.py. This code:
- Checks whether a folder contains
pqc_encrypted.bin,ct.binandprivate_key.bin - Takes the key and ciphertext and extracts the secret code
- Decrypts and saves the secret code in
pqc_decrypted.txt
And here’s the outcome:
python3 decrypt_pqc.py
File decrypted successfully! Output: pqc_decrypted.txt
Decrypted file contents: Hello, this is a secret!Preparing the environment for PQC testing
To practically evaluate the security of data transmission, we’ll use a local Docker-based environment. This approach makes it possible to reproduce a realistic network model with multiple nodes without relying on physical machines or virtual servers.
Note: We’re running this experiment on macOS, but the architecture itself is platform-independent.
1. Prepare the working directory for Docker on the host system, and mount this directory into the Docker containers:
mkdir -p ~/pqc_labNow, we copy required files into this directory:
cp /Users/user/PCQ/* ~/pqc_lab/And run verification:
ls -lh ~/pqc_labExpected output:
-rw-r--r--@ 1 user staff 768 Feb 6 11:08 ct.bin
-rw-r--r--@ 1 user staff 22 Feb 6 11:08 pqc_encrypted.bin
-rw-r--r--@ 1 user staff 22 Feb 6 11:08 pqc.txt
-rw-r--r--@ 1 user staff 1632 Feb 6 11:08 private_key.bin
-rw-r--r--@ 1 user staff 800 Feb 6 11:08 public_key.bin2. Create a Docker network:
docker network create pqcnetThe pqcnet network acts as a private LAN inside Docker, as each container receives a private IP address, containers can communicate directly over TCP/IP, and traffic remains inside a controlled subnet.
The main reason for this setup is experimental control: we intentionally create a small internal network where traffic can be intercepted and analyzed under a passive attacker model.
3. Launch machines. Machine A (Sender) represents the sending endpoint:
docker run -d --name machine-a \
--network pqcnet \
-v ~/pqc_lab:/lab \
ubuntu:22.04 sleep infinityMachine B (Receiver) represents the receiving endpoint:
docker run -d --name machine-b \
--network pqcnet \
-v ~/pqc_lab:/lab \
ubuntu:22.04 sleep infinity4. Launch an Observer (passive sniffer) that represents a passive monitoring attacker:
docker run -d --name observer \
--network container:machine-b \
-v ~/pqc_lab:/lab \
ubuntu:22.04 sleep infinity5. Install network tools. To keep the setup simple, we can only install two tools:
netcat (nc)on Machine A and Machine B to transmit raw TCP byte streams without encryption or application-level protocol overheadtcpdumpon Observer to capture packets in PCAP format for later offline analysis in Wireshark
docker exec -it machine-a bash -lc "apt update && apt install -y netcat-openbsd"
docker exec -it machine-b bash -lc "apt update && apt install -y netcat-openbsd"
docker exec -it observer bash -lc "apt update && apt install -y tcpdump iproute2"Let’s run verification:
docker exec -it machine-a bash -lc "which nc"
docker exec -it machine-b bash -lc "which nc"
docker exec -it observer bash -lc "which tcpdump"Expected output:
/usr/bin/nc
/usr/bin/nc
/usr/bin/tcpdump6. Verify test file availability. Before running the network experiment, we need to validate the plaintext input inside the containers. This way, we’ll ensure that the mounted dataset is accessible and consistent.
docker exec -it machine-a bash -lc "cat /lab/pqc.txt"Expected output:
Hello, it's a secret!At this point, preparations are finally done, and we can start PQC security testing.
Read also
Shifting to Post-Quantum Cryptography in Automotive Projects: Reasons, Challenges, Considerations
Gain insights into applying quantum-resistant encryption to automotive systems and ensuring compliance with evolving security expectations. Learn how to enhance protection across connected and autonomous vehicle components.

Note: We usually start by establishing a baseline: testing plaintext transmission with passive traffic interception. Such a test demonstrates how easily data confidentiality fails when plaintext is transmitted over a network. To spare you the details, let’s just show you the results (Screenshot 1) and move to more interesting tests.

Testing scenario 1: Post-quantum encrypted transmission with passive traffic interception
Let’s evaluate whether a passive observer — capable of capturing full packet payloads — can reconstruct the original message when transmitted data is encrypted using a post-quantum scheme (Kyber512 + symmetric masking).
Note: In these tests, we continue working with ML-KEM-512. Kyber was the name used during the NIST PQC competition (specifically, CRYSTALS-Kyber). When standardized, NIST renamed it to ML-KEM (Module-Lattice KEM). In simple terms: Kyber512 = ML‑KEM‑512 under the FIPS 203 standard.
1. Prepare packet capture on Observer (port 9999):
docker exec -d observer bash -lc \
"tcpdump -i eth0 -s 0 -nn -U -c 50 \
-w /lab/s2_pqc_encrypted_observer.pcap 'tcp port 9999'"Why these parameters matter:
-i eth0→ capture on receiver interface-s 0→ capture full payload (not truncated)-nn→ disable name resolution (raw IP/port)-U→ write packets immediately (prevents corrupted files)-c 50→ stop automatically after capture completes
This ensures a clean, finalized PCAP file suitable for forensic analysis.
2. Start the encrypted Receiver listener (Machine B):
docker exec -d machine-b bash -lc \
"cd /lab; rm -f recv_pqc_encrypted.bin; nc -l -p 9999 > recv_pqc_encrypted.bin"At this stage:
- The receiver is waiting
- Observer is capturing
- No data has yet been transmitted
3. Send the encrypted file from Machine A:
docker exec -it machine-a bash -lc \
"cd /lab; nc -q 1 172.18.0.2 9999 < pqc_encrypted.bin"Let’s break down the code:
- 172.18.0.2 is a private IP address of Machine B
- 9999 is a dedicated port for encrypted transmission
- -q 1 ensures clean connection termination
- < pqc_encrypted.bin sends raw binary payload
No TLS or transport-layer encryption is used. The only protection applied is at the payload level.
4. Verify correct reception on Machine B:
docker exec -it machine-b bash -lc \
"ls -lh /lab/recv_pqc_encrypted.bin; \
wc -c /lab/recv_pqc_encrypted.bin /lab/pqc_encrypted.bin"Expected outcome:
- File sizes match exactly
- No truncation
- Transmission integrity confirmed
This proves that the encrypted payload was delivered correctly end to end.
5. Confirm that Observer recorded the traffic:
docker exec -it observer bash -lc \
"ls -lh /lab/s2_pqc_encrypted_observer.pcap"If file size > 0, capture succeeded.
6. Export PCAP to macOS:
docker cp observer:/lab/s2_pqc_encrypted_observer.pcap ~/pqc_lab/7. Run Wireshark analysis (offline):
pen ~/pqc_lab/s1_plain_observer.pcapThe PCAP file is opened in Wireshark on macOS. Steps in Wireshark:
- Right-click on any packet → Follow → TCP Stream
- Select ASCII mode

This time, we see:
- No readable message appears
- Payload appears as high-entropy binary data
- No recognizable ASCII fragments
- No semantic content is recoverable
Read also
Future-proof Your FinTech Business with Post-Quantum Cryptography
Learn how to prepare your financial and banking solutions for post-quantum challenges and reduce exposure to cryptographic risks. Get actionable guidance for building resilient and secure systems.

Testing scenario 2: Interception of pqc_encrypted.bin and ct.bin without the private key
The goal of Scenario 2 is to model a realistic passive attacker who intercepts a feasible set of artifacts transmitted over the network, including:
pqc_encrypted.bin(encrypted payload)ct.bin(KEM ciphertext)
However, an attacker doesn’t have private_key.bin. This scenario demonstrates that interception doesn’t equal decryption. Even with both intercepted files, an attacker can’t recover the shared secret (and therefore can’t reconstruct plaintext) because decapsulation requires the private key.
Here’s data we’ll use for this testing scenario:
pqc_encrypted.binis encrypted payload (masked using the derived shared secret)ct.binis Kyber512 ciphertext (KEM output)private_key.binexists only on the Receiver side (Machine B) and is not available to the attacker
1. Prepare packet capture on Observer (port 7777):
docker exec -d observer bash -lc \
"tcpdump -i eth0 -s 0 -nn -U -c 50 \
-w /lab/s3_ct_observer.pcap 'tcp port 7777'"This produces a stable PCAP artifact for later analysis in Wireshark. Using -U + -c 50 prevents truncated/empty captures.
2. Start Receiver listener on Machine B (for ct.bin):
docker exec -d machine-b bash -lc \
"cd /lab; rm -f recv_ct.bin; nc -l -p 7777 > recv_ct.bin"At this moment:
- Receiver is waiting on port 7777
- Observer is sniffing
- No data has been transmitted yet
3. To send ct.bin from Machine A to Machine B, first get the IP:
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' machine-bSend ct.bin:
docker exec -it machine-a bash -lc \
"cd /lab; nc -q 1 172.18.0.2 7777 < ct.bin"4. Verify successful reception of ct.bin on Machine B:
docker exec -it machine-a bash -lc \
"cd /lab; nc -q 1 172.18.0.2 7777 < ct.bin"Here, we expect file sizes to be equal and transmission integrity to be confirmed.
5. Ensure that Observer captured traffic:
docker exec -it observer bash -lc \
"ls -lh /lab/s3_ct_observer.pcap"6. Export PCAP to the host and analyze it using Wireshark:
docker cp observer:/lab/s3_ct_observer.pcap ~/pqc_lab/Screenshot 3 shows the results:

What an attacker observes:
In TCP Stream, the attacker sees a sequence of bytes with no readable structure. This is expected because ct.bin is not plaintext and the Kyber output is KEM ciphertext (cryptographic binary material).
Scenario 2 demonstrates that:
- A passive MITM attack can intercept both the encrypted payload and Kyber ciphertext.
- Captured data is insufficient to recover the shared secret.
- Decryption is only feasible for the legitimate receiver holding the private key.
Evaluating test results
The key result demonstrated by our experiments is that even with full MITM visibility, attackers can’t reconstruct the plaintext or derive the symmetric secret.
Since encryption takes place on the sender’s side before data enters the network, attackers only see the results of cryptographic transformations.
The private_key.bin is never transmitted, making decapsulation impossible. Thus, MITM attacks threaten communication availability but don’t compromise confidentiality in this PQC architecture.
Related project
Developing a Custom Secrets Management Desktop Application for Secure Password Sharing and Storage
Find out how we created a tailored secrets management application that enhanced data security, simplified credential handling, and helped the client reduce risks associated with sensitive information exposure.

Ensure secure and efficient PQC adoption with Apriorit
With 20+ years of experience in cybersecurity and software development, we are continuously expanding our expertise. Apriorit experts have been researching and working with PQC algorithms for several years now and keep going.
Apriorit teams know what PQC algorithms to use for different projects, how to implement them successfully, and — most importantly 一 how to test algorithm correctness, security, and performance. Delegate PQC testing to our specialists to make sure your strategic security investments truly work for you.
Choose Apriorit for:
- Hands-on experience implementing post-quantum cryptography in software projects, as we help clients leverage new technologies to meet business and security goals
- Expertise in testing PQC implementations with a focus on checking algorithms’ implementation, performance, security, and impact on the user experience
- Strong cybersecurity engineering skills to help you assess your product protection and suggest meaningful and relevant enhancements
- Cross-industry experience, assisting clients in automotive, FinTech, IoT, and other spheres with advanced encryption and protection measures
Post-quantum cryptography promises protection against future quantum-enabled attacks, but deploying it safely is far from trivial. For cybersecurity vendors and enterprises building long-lived digital systems, adopting PQC without rigorous testing introduces significant operational and security risks.
At Apriorit, we’re ready to help your team and product embrace new technologies with caution, accurate testing, and actual results.
Planning for secure systems in the quantum era?
Work with our team to uncover hidden vulnerabilities, validate PQC implementations, and align your product with modern security standards.
FAQ
Why isn’t traditional cryptographic testing enough for post‑quantum systems?
<p>Traditional tests will miss vulnerabilities specific to PQC implementations because:</p>
<ul class=apriorit-list-markers-green>
<li>Traditional testing focuses on RSA/ECC behaviors, key sizes, timing, and compatibility that no longer apply in the same way to complex PQC algorithms.</li>
<li>PQC introduces larger key and ciphertext sizes, different memory patterns, new failure modes, hybrid negotiation complexities, and different side‑channel risks. All of these require testing post-quantum cryptography implementations in a new way.</li>
</ul>
What are the main types of PQC testing?
<p>The PQC testing workflow can vary for different projects, but most commonly it includes the following tests:</p>
<ul class=apriorit-list-markers-green>
<li>Cryptographic correctness testing: verifying that key generation, encapsulation/decapsulation, and signature verification work as intended</li>
<li>Interoperability testing: ensuring PQC works across devices, protocols, and hybrid classical + PQC configurations</li>
<li>Performance and resource benchmarking: measuring handshake latency, CPU use, memory footprint, and packet sizes</li>
<li>Security testing under active attacks: MITM, downgrade attempts, replay attacks, malformed ciphertext injection, etc.</li>
<li>Resilience testing: verifying behavior under packet loss, buffer pressure, network fragmentation, and error edges</li>
</ul>
How do post‑quantum emulators support testing and migration planning?
<p>Post‑quantum emulators like liboqs and OQS‑OpenSSL let teams test PQC algorithms as if quantum‑capable adversaries already existed (being sort of a quantum computing simulator), providing:</p>
<ul class=apriorit-list-markers-green>
<li>Stable implementations of Kyber, Dilithium, SPHINCS+, and other algorithms</li>
<li>Ability to run real-world experiments such as traffic interception, failure injection, hybrid handshake analysis, and performance benchmarking</li>
<li>Ability to run tests without requiring physical quantum hardware</li>
</ul>
Can attackers still perform meaningful attacks on PQC‑encrypted systems today?
<p>Yes, attackers may target the implementation around PQC, aiming to bypass or misuse the system rather than decrypt the data. For example:</p>
<ul class=apriorit-list-markers-green>
<li>A poorly implemented hybrid mode may allow a downgrade to classical crypto.</li>
<li>Malformed ciphertext handling may cause information leaks or crashes.</li>
<li>Replay or MITM attempts can manipulate system behavior even if the attacker can’t read the encrypted content.</li>
</ul>
<p>In all these cases, the goal is to exploit weaknesses in how PQC is integrated, validated, or used within your application.</p>
