Metadata-Version: 2.4
Name: pqc-scanner
Version: 1.3.0
Summary: Automated timing side-channel scanner for NIST PQC standards ML-KEM and ML-DSA
Author-email: Collective Qubits <disha.p2023@vitstudent.ac.in>
License: MIT License
        
        Copyright (c) 2025 Collective Qubits
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/Disha23112004/pqc-scanner
Project-URL: Repository, https://github.com/Disha23112004/pqc-scanner
Project-URL: Issues, https://github.com/Disha23112004/pqc-scanner/issues
Project-URL: Documentation, https://github.com/Disha23112004/pqc-scanner#readme
Keywords: post-quantum cryptography,side-channel,timing attack,ML-KEM,ML-DSA,FIPS 203,FIPS 204,TVLA,security,cryptography
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Scientific/Engineering
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: POSIX :: Linux
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: kyber-py>=0.5.0
Requires-Dist: dilithium-py>=0.3.0
Requires-Dist: scipy>=1.10.0
Requires-Dist: numpy>=1.24.0
Requires-Dist: click>=8.1.0
Requires-Dist: rich>=13.0.0
Dynamic: license-file

# PQC Side-Channel Scanner

[![CI](https://github.com/Disha23112004/pqc-scanner/actions/workflows/scan.yml/badge.svg)](https://github.com/Disha23112004/pqc-scanner/actions/workflows/scan.yml)
[![PyPI version](https://badge.fury.io/py/pqc-scanner.svg)](https://pypi.org/project/pqc-scanner/)
[![PyPI downloads](https://img.shields.io/pypi/dm/pqc-scanner)](https://pypi.org/project/pqc-scanner/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/)

An open-source automated timing side-channel scanner for NIST post-quantum cryptography standards. Detects constant-time violations in ML-KEM (FIPS 203) and ML-DSA (FIPS 204) implementations using TVLA statistical methodology.

Built by [Disha P](https://github.com/Disha23112004), Technical Director at [Collective Qubits](https://collectivequbits.com).

---

## Why This Exists

NIST finalized the post-quantum cryptography standards in August 2024. Banks, cloud providers, and government agencies are actively deploying ML-KEM and ML-DSA right now to protect against future quantum attacks. But timing side-channel vulnerabilities in cryptographic implementations can leak secret key material to an attacker — even when the underlying algorithm is mathematically secure.

Existing tools like dudect require manual C-level integration, have no PQC-specific targets, and predate the NIST standards by years. No open-source automated scanner existed for FIPS 203 and FIPS 204 specifically.

This tool fills that gap.

---

## What It Found

### Python layer — ML-KEM-768 (kyber-py)

| Operation | t-statistic | Verdict |
| --- | --- | --- |
| Decapsulation | -3868 | CRITICAL LEAK |
| Encapsulation | -3377 | CRITICAL LEAK |
| KeyGen | 3.1 | PASS |

> **Note:** kyber-py is a documented non-constant-time reference implementation. These findings are expected and not novel.

### Python layer — ML-DSA-65 (dilithium-py)

| Operation | t-statistic | Verdict |
| --- | --- | --- |
| Sign | 0.4 | PASS |
| Verify | 15.4 | LEAK |
| KeyGen | 7.1 | LEAK |

---

### FIPS 203 — ML-KEM (all parameter sets), liboqs 0.15.0, 100k traces

| Algorithm | Operation | t-statistic | Delta | Verdict |
| --- | --- | --- | --- | --- |
| ML-KEM-512 | Encapsulation | -4.9 | 250ns | CRITICAL |
| ML-KEM-512 | Decapsulation | 0.5 | 39ns | PASS |
| ML-KEM-768 | Encapsulation | 75.5 | 264ns | CRITICAL |
| ML-KEM-768 | Decapsulation | -3.2 | 39ns | PASS |
| ML-KEM-1024 | Encapsulation | -12.6 | 1011ns | CRITICAL |
| ML-KEM-1024 | Decapsulation | -0.4 | 45ns | PASS |

**Bare metal confirmed (GitHub Actions ubuntu-latest):**

| Algorithm | Operation | t-statistic | Delta | Verdict |
| --- | --- | --- | --- | --- |
| ML-KEM-512 | Encapsulation | -8.37 | 87ns | CRITICAL |
| ML-KEM-768 | Encapsulation | -13.68 | 298ns | CRITICAL |
| ML-KEM-1024 | Encapsulation | -51.99 | 517ns | CRITICAL |

**Root cause — encaps_derand (RNG bypassed, same public key):**

| Build | t-statistic | Delta | Verdict |
| --- | --- | --- | --- |
| Portable C | 0.66 | 7ns | PASS |
| AVX2 | 0.93 | 15ns | PASS |

**Interpretation:** Encapsulation leaks across all three ML-KEM parameter sets on both WSL2 and bare metal. Root cause confirmed as RNG-induced — `OQS_randombytes()` inside `OQS_KEM_encaps()` has variable timing. When bypassed via `encaps_derand()`, both builds pass. The cryptographic core is clean. Decapsulation passes on all parameter sets.

---

### FIPS 204 — ML-DSA (all parameter sets), liboqs 0.15.0, 100k traces

| Algorithm | Operation | t-statistic | Verdict |
| --- | --- | --- | --- |
| ML-DSA-44 | Sign | 0.21 | PASS |
| ML-DSA-44 | Verify | -0.59 | PASS |
| ML-DSA-65 | Sign | 0.13 | PASS |
| ML-DSA-65 | Verify | -1.02 | PASS |
| ML-DSA-87 | Sign | -0.38 | PASS |
| ML-DSA-87 | Verify | -0.64 | PASS |

**Interpretation:** ML-DSA is clean across all three parameter sets and both sign and verify operations.

---

### FIPS 206 — FN-DSA/FALCON (all variants), liboqs 0.15.0, 100k traces

| Algorithm | Operation | t-statistic | Delta | Verdict |
| --- | --- | --- | --- | --- |
| FALCON-512 | Sign | 1.55 | 2186ns | PASS |
| FALCON-512 | Verify | -6.92 / -10.04 | ~6100ns | CRITICAL |
| FALCON-1024 | Sign | 1.03 | 2940ns | PASS |
| FALCON-1024 | Verify | -9.81 / -10.58 | ~9500ns | CRITICAL |
| FALCON-padded-512 | Verify | -9.95 | 7689ns | CRITICAL |
| FALCON-padded-1024 | Verify | -11.97 | 9637ns | CRITICAL |

**Root cause investigation:**
- Same message, different signatures → still CRITICAL (t=-53.86)
- Padded variants (fixed-length signatures) → still CRITICAL
- Verify has no RNG call — cannot be RNG-induced
- **Conclusion: Signature-content dependent timing in FALCON verify. Algorithmic leak.**

**Interpretation:** FALCON verify leaks across all four variants including padded (fixed-length) variants. The leak persists with the same message and different signatures, confirming it is signature-content dependent, not message or length dependent. Signing passes on all variants. No open issue exists for this finding on the liboqs GitHub.

---

### FIPS 205 — SLH-DSA (all parameter sets), liboqs 0.15.0, 50k traces

SLH-DSA verify shows variable timing across parameter sets (some CRITICAL, some PASS). This is consistent with the inherently variable-time nature of Merkle tree traversal in hash-based signatures and is expected behavior by design, not an implementation vulnerability.

---

**C-level noise floor (AES-128):** 11.7ns. ML-KEM encapsulation finding is 22× above noise floor on WSL2, confirmed on bare metal GitHub Actions runners.

## How It Works

The scanner implements Test Vector Leakage Assessment (TVLA) — the same methodology used by Riscure and documented in ISO/IEC 17825. For each cryptographic operation it:

1. Collects timing measurements for a **fixed input** (same value every time)
2. Collects timing measurements for **random inputs** (different every time)
3. Runs **Welch's t-test** on the two distributions
4. Flags as leaking if **|t| > 4.5** — the ISO 17825 threshold

The scanner operates at two measurement layers:

- **Python layer** — targets kyber-py and dilithium-py via `time.perf_counter_ns()`, detects millisecond-level leaks
- **C layer** — targets liboqs directly with `CLOCK_MONOTONIC_RAW`, detects nanosecond-level leaks

---

## Installation

### Quickstart

```bash
pip install pqc-scanner
```

### Docker (no setup required)

```bash
docker build -t pqc-scanner .
docker run pqc-scanner python3 cli.py liboqs --traces 50000
docker run pqc-scanner python3 cli.py derand --traces 100000
```

No liboqs installation, no gcc, no Python setup required. Everything is bundled.

### From source (recommended for full C harness support)

#### Requirements

- Ubuntu 20.04+ or WSL2 (Ubuntu 24.04 tested)
- Python 3.10+
- gcc and clang
- liboqs (instructions below)

#### 1. Clone the repository

```bash
git clone https://github.com/Disha23112004/pqc-scanner
cd pqc-scanner
```

#### 2. Set up Python environment

```bash
python3 -m venv venv
source venv/bin/activate
pip install kyber-py dilithium-py scipy numpy click rich
```

#### 3. Install liboqs

```bash
sudo apt install -y cmake ninja-build libssl-dev clang

git clone --depth=1 https://github.com/open-quantum-safe/liboqs ~/liboqs
cmake -S ~/liboqs -B ~/liboqs/build -DBUILD_SHARED_LIBS=ON -GNinja
cmake --build ~/liboqs/build --parallel 4
sudo cmake --build ~/liboqs/build --target install
sudo ldconfig
```

#### 4. Build the C harnesses

```bash
cd harness

# Production scanner (liboqs ML-KEM-768)
gcc -O2 -o ml_kem_harness ml_kem_harness.c -loqs -lssl -lcrypto

# Noise floor baseline (AES-128)
gcc -O2 -o baseline_harness baseline_harness.c -lssl -lcrypto

cd ..
```

---

## Usage

Always activate the virtual environment first:

```bash
source venv/bin/activate
```

### Production scan (liboqs C library) — primary scan

```bash
python cli.py liboqs --traces 100000
```

Uses `CLOCK_MONOTONIC_RAW` via C harness for nanosecond precision. 100,000 traces recommended for statistical confidence.

### Scan ML-KEM and ML-DSA (Python targets)

```bash
python cli.py scan --algorithm both --traces 10000 --pin-cpu
```

Options:
- `--algorithm` — `ml-kem`, `ml-dsa`, or `both`
- `--traces` — number of measurements per operation (default 10000)
- `--pin-cpu` — pin process to CPU core 0 for lower noise
- `--no-exit` — do not exit with code 1 on findings (for scripting)
- `--open-html` — open HTML report in browser automatically

### Algorithm comparison

```bash
python cli.py compare --traces 10000 --pin-cpu
```

Scans ML-KEM-768 and ML-DSA-65 side by side. Produces a comparison HTML report with bar chart.

### Python noise floor characterization

```bash
python cli.py baseline --traces 10000 --pin-cpu
```

Runs SHA-256 as a control experiment. SHA-256 is provably constant-time so any signal here is measurement noise.

### C-level noise floor characterization

```bash
python cli.py c-baseline --traces 100000
```

Runs AES-128 (AES-NI hardware instruction) through the same C harness. Reports the noise floor delta so you can assess signal-to-noise ratio of liboqs findings.

### RNG root cause isolation (derand)
python cli.py derand --traces 100000
Bypasses the OS RNG using encaps_derand() with a fixed seed. PASS = leak is RNG-induced. LEAK = algorithmic. Run after liboqs scan to confirm root cause.

Compiler flag sweep

```bash
python cli.py compiler-sweep --traces 50000 --runs 3
```

Builds the C harness with six compiler flag combinations and scans each. Searches the same optimization space where KyberSlash was found.

---

## Output

Every scan produces:

- `*.json` — structured findings for CI/CD integration
- `*.html` — interactive report with timing distribution histograms

### JSON format

```json
{
  "scanner": "pqc-scanner v1.1.3",
  "target": "liboqs ML-KEM-768",
  "summary": {
    "total": 1,
    "critical": 1,
    "high": 0,
    "medium": 0
  },
  "findings": [
    {
      "operation": "ML-KEM Encapsulation",
      "t_statistic": 75.5185,
      "p_value": 0.0,
      "delta_ns": 264.0,
      "severity": "CRITICAL",
      "verdict": "LEAK DETECTED"
    }
  ]
}
```

---

## CI/CD Integration

The scanner exits with code 1 when findings are detected:

```yaml
# GitHub Actions example
- name: PQC Timing Scan
  run: |
    source venv/bin/activate
    python cli.py liboqs --traces 50000
  # Pipeline fails automatically if leaks are found
```

Use `--no-exit` to suppress the exit code in reporting-only mode.

### SARIF output (GitHub Advanced Security)

```bash
python cli.py liboqs --traces 100000 --format sarif --output results
```

```yaml
- name: PQC Timing Scan
  run: python cli.py liboqs --traces 50000 --no-exit --format sarif --output pqc

- name: Upload to GitHub Security
  uses: github/codeql-action/upload-sarif@v4
  with:
    sarif_file: pqc.sarif
```

Findings appear under Security → Code scanning in your repository.

---

## Methodology

### TVLA (Test Vector Leakage Assessment)

Developed by Cryptography Research Inc. and documented in ISO/IEC 17825. The core principle: if an implementation is constant-time, its execution time should be statistically independent of the input.

### Welch's t-test

Used instead of Student's t-test because it does not assume equal variance between distributions. Threshold: |t| > 4.5 indicates statistically significant timing leakage.

### Derandomization (derand) technique

A novel root-cause isolation method introduced in this tool. By calling `encaps_derand()` directly with a fixed 32-byte seed, bypassing `OQS_randombytes()` entirely, RNG-induced timing variation is eliminated from the measurement window. Comparing standard TVLA results against derand results distinguishes algorithmic leakage from RNG-induced leakage — a distinction critical for correct remediation. This technique is not present in prior published tooling.

### Measurement precision

- Python layer — `time.perf_counter_ns()`, noise floor ~100–130ns on WSL2
- C layer — `CLOCK_MONOTONIC_RAW` via `clock_gettime()`, noise floor ~10–12ns on WSL2

### Noise reduction

- **500-trace warmup** — discards initial measurements to stabilize CPU cache and branch predictor
- **5% outlier trimming** — removes top 5% of measurements to eliminate OS interrupt spikes
- **CPU affinity** — `sched_setaffinity` pins process to core 0 to reduce scheduler noise
- **Pre-generated test vectors** — random inputs generated before the timed window so `os.urandom()` does not contaminate measurements

### WSL2 limitations

WSL2 adds ~10–130ns of hypervisor noise depending on system load. Sub-50ns signals require bare metal Linux for confirmation. The decapsulation finding at t=-3.2 was not stable across runs and cannot be confirmed.

---

## Project Structure

```
pqc-scanner/
├── cli.py                      ← all commands
├── requirements.txt
├── src/pqc_scanner/
│   ├── scanner/
│   │   ├── timing.py           ← TVLA engine, warmup, outlier trim, CPU affinity
│   │   ├── static.py           ← static analysis for crypto context
│   │   └── report.py           ← JSON and HTML report generation
│   └── targets/
│       ├── ml_kem_target.py    ← ML-KEM-768 Python harness (kyber-py)
│       ├── ml_dsa_target.py    ← ML-DSA-65 Python harness (dilithium-py)
│       ├── baseline_target.py  ← SHA-256 control experiment
│       └── liboqs_target.py    ← liboqs subprocess interface
└── harness/
    ├── ml_kem_harness.c            ← C timing harness, CLOCK_MONOTONIC_RAW
    ├── ml_kem_harness_derand.c     ← derand harness for RNG root cause isolation
    ├── ml_dsa_harness.c            ← ML-DSA-65 sign/verify harness
    └── baseline_harness.c          ← AES-128 noise floor harness
```

---

## Comparison With Existing Tools

| Capability | dudect | Riscure | This tool |
|------------|--------|---------|-----------|
| ML-KEM / ML-DSA targets | ✗ | ✗ | ✓ |
| liboqs integration | ✗ | ✗ | ✓ |
| Python + C dual layer | ✗ | ✗ | ✓ |
| Derand root-cause isolation | ✗ | ✗ | ✓ |
| Automated HTML reporting | ✗ | ✓ (hardware) | ✓ |
| CI/CD exit codes | ✗ | ✗ | ✓ |
| Noise floor validation | ✗ | ✓ (hardware) | ✓ |
| No hardware required | ✓ | ✗ | ✓ |
| Single command scan | ✗ | ✗ | ✓ |
| Open source | ✓ | ✗ | ✓ |

---

## Related Work

- **dudect** — Reparaz et al. (2016). General-purpose constant-time testing library in C. Requires source-level integration. No PQC targets.
- **KyberSlash** — Bernstein et al. (2024). Manual timing analysis finding secret-dependent division leak in Kyber implementations.
- **TVLA** — Cryptography Research Inc. (2011). Original leakage assessment methodology, standardized in ISO/IEC 17825.
- **ISO/IEC 17825:2024** — Updated standard for testing non-invasive attack mitigations in cryptographic modules.
- **liboqs** — Open Quantum Safe project. C library for post-quantum cryptographic algorithms.

---

## Limitations

- WSL2 hypervisor adds noise. Sub-50ns findings require bare metal Linux for confirmation.
- kyber-py and dilithium-py are documented as non-constant-time reference implementations. Python layer findings for these targets are expected and not novel.
- liboqs findings are statistically confirmed. Exploitability in a real network attack scenario requires further research beyond timing measurement.
- Compiler sweep results below 50ns delta on WSL2 are marked inconclusive.
- TVLA detects first-order leakage. Higher-order leakage in masked implementations may require higher-order TVLA extensions not yet implemented.
- A PASS result does not prove constant-time behavior — it means no significant signal was detected at the given trace count and threshold.

---

## License

MIT License. See [LICENSE](LICENSE) for details.

---

## Citation

If you use this tool in research, please cite:

```
Disha P, "PQC Side-Channel Scanner: Automated TVLA-based timing analysis for 
NIST post-quantum cryptography standards," 2026.
https://github.com/Disha23112004/pqc-scanner
```
