# qbit404 — full corpus
Site: https://qbit404.com
Generated: 2026-06-03T15:25:57+00:00
License: free to cite and quote with attribution. No third-party cookies.
---
## Language: en
### Grover bites AES: why 128 is dead and 256 is your new baseline
- URL: https://qbit404.com/en/news/grover-aes
- Date: 2026-06-02
- Author: Riku Saari
- Tags: grover, aes, symmetric, migration, key-size
## TL;DR
A quantum adversary running Grover's algorithm against an `n`-bit symmetric
cipher needs about **2^(n/2)** quantum operations to recover the key. AES-128
falls to **~2⁶⁴** effective work — within reach of a serious adversary in
the 2030s. AES-256 falls to **~2¹²⁸** effective — beyond every projected
budget. **Move every AES-128 key to AES-256 now.** This is the cheapest PQ
upgrade you will ever do.
## 1. The math, compressed
Grover gives a **square-root** speedup on unstructured search. For a key
space of size `2^n`:
- Classical brute-force: `2^n` trials, `~2^n` quantum gates.
- Quantum (Grover): `~2^(n/2)` iterations, each costing `~O(n)` reversible
gates plus an oracle evaluation.
Concretely, for AES:
| Cipher | Classical effort | Grover effort | Verdict |
|---------|------------------|----------------|---------|
| AES-128 | 2¹²⁸ | 2⁶⁴ | Falls in 2030s |
| AES-192 | 2¹⁹² | 2⁹⁶ | Borderline |
| AES-256 | 2²⁵⁶ | 2¹²⁸ | Safe |
The 2⁶⁴ number is roughly the cost of mining the Bitcoin chain twice. Not
trivial — but predictable, parallelizable, and within reach of a state-level
adversary by the late 2020s.
NSA's CNSA 2.0 advisory (2022) explicitly drops AES-128 from approved
suites for "long-term confidentiality of national security information".
The migration target is **AES-256-GCM**.
## 2. Why hashes are mostly OK
The same speedup applies to hash preimages. SHA-256 preimage cost drops from
2²⁵⁶ to ~2¹²⁸ under Grover — still safe. Collision attacks (birthday) are
harder under quantum: Brassard-Høyer-Tapp gives a cubic-root, so SHA-256
collisions drop from 2¹²⁸ to ~2⁸⁵.
That **collision** number matters for long-lived commitments — Merkle roots
in append-only logs, certificate-transparency entries, deduplicated storage.
For those use cases, **migrate to SHA-384 or SHA3-384**. SHA-256 is fine for
short-lived integrity (TLS record MAC, HMAC of session tokens).
## 3. Migration: every place you wrote AES-128, sed it
Most teams have AES-128 sprinkled across three places:
1. **TLS cipher suites** (server config, load balancer config).
2. **Application-level envelope encryption** (KMS calls, database column
encryption, file-at-rest).
3. **Internal RPC encryption** (Wireguard, Tailscale, custom HMAC + AES).
### 3a. Python (cryptography)
```python
# cryptography ≥ 41
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
# BEFORE: AES-128 — Grover-vulnerable
key128 = AESGCM.generate_key(bit_length=128)
aead128 = AESGCM(key128)
# AFTER: AES-256 — Grover-safe
key256 = AESGCM.generate_key(bit_length=256)
aead256 = AESGCM(key256)
nonce = os.urandom(12)
ct = aead256.encrypt(nonce, b'plaintext', b'associated-data')
pt = aead256.decrypt(nonce, ct, b'associated-data')
```
### 3b. Node.js
```javascript
const crypto = require('node:crypto');
// BEFORE: aes-128-gcm
function encryptOld(key, plaintext) {
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-128-gcm', key, iv);
const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
return { iv, tag: cipher.getAuthTag(), ct };
}
// AFTER: aes-256-gcm (only the algorithm string and key length change)
function encryptNew(key, plaintext) {
// key must be 32 bytes
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
return { iv, tag: cipher.getAuthTag(), ct };
}
// Generate a fresh 256-bit key
const key256 = crypto.randomBytes(32);
```
### 3c. PHP (libsodium)
```php
/dev/null
echo
echo "=== bit_length=128 generators ==="
grep -rnE 'bit_length\s*=\s*128|key.*=.*16\s*[);]|randomBytes\(\s*16\s*\)' \
--include='*.{py,js,ts,php,go,rb,rs}' "$ROOT" 2>/dev/null
echo
echo "=== TLS configs referencing CBC suites ==="
grep -rnE 'AES.*CBC|CBC-SHA' \
--include='*.{conf,nginx,htaccess,yaml,yml}' "$ROOT" 2>/dev/null
```
Make it executable and run against your repo: `chmod +x aes256-migrate.sh &&
./aes256-migrate.sh /srv/app`. Triage by file.
## 5. Cost of waiting
A document encrypted with AES-128 in 2026 will be openable by a state-level
adversary with quantum capacity around **2032-2034**. If your retention
policy says "encrypted for 7 years", the math says you are already late.
The migration to AES-256 has no protocol breakage. No new dependencies. No
performance regression worth measuring (AES-NI on Intel/AMD makes 256 nearly
indistinguishable from 128 at the throughput you care about). This is the
single cheapest post-quantum upgrade in the entire stack.
## 6. References
- Bernstein, *Cost analysis of hash collisions: will quantum computers make
SHARCS obsolete?* (2009)
- Grassl, Langenberg, Roetteler & Steinwandt, *Applying Grover's algorithm
to AES: quantum resource estimates* (2016)
- NSA CNSA 2.0 (2022, updated 2024) — mandates AES-256-GCM for new builds
- NIST SP 800-131A Rev. 2 — retires AES-128 for "transition" use cases
> Migrate symmetric first. It costs nothing. The expensive PQ work
> (asymmetric, signatures, KEMs) deserves your attention after.
---
### Inside the NIST PQC suite: ML-KEM (FIPS 203), ML-DSA (FIPS 204), SLH-DSA (FIPS 205)
- URL: https://qbit404.com/en/news/nist-pqc-suite
- Date: 2026-06-02
- Author: Mara Chen
- Tags: nist, fips, ml-kem, ml-dsa, slh-dsa, kyber, dilithium, sphincs
## TL;DR
NIST published three final standards in 2024-2025:
- **FIPS 203 — ML-KEM** (Module-Lattice Key Encapsulation, née Kyber)
- **FIPS 204 — ML-DSA** (Module-Lattice Digital Signature, née Dilithium)
- **FIPS 205 — SLH-DSA** (Stateless Hash-based Digital Signature, née SPHINCS+)
A fourth (**FIPS 206 — FN-DSA**, née Falcon) is in draft, expected 2026.
Every cryptographic library you reach for must by now expose the FIPS names
and parameter sets. If yours doesn't, it is out of date.
## 1. ML-KEM — FIPS 203
**What it does**: encapsulates a 32-byte shared secret under a recipient's
public key. Drop-in replacement for ECDH or RSA-KEM. Used in TLS hybrid
KEX, JWE key wrap, MLS group key derivation.
**Security reduction**: hardness of Module-LWE (Learning With Errors over
module lattices). Best known classical attack: ~ 2^l/2 for parameter level
l, no quantum speedup beyond Grover.
**Parameter sets**:
```
parameter sec. cat pub key ciphertext shared secret
─────────────────────────────────────────────────────────────
ML-KEM-512 NIST L1 800 B 768 B 32 B
ML-KEM-768 NIST L3 1184 B 1088 B 32 B
ML-KEM-1024 NIST L5 1568 B 1568 B 32 B
```
Pick **ML-KEM-768** by default. L3 is the recommended baseline; L5 only if
you're encrypting nation-state archives.
### Smallest possible call (Python)
```python
# pip install pqcrypto
from pqcrypto.kem import ml_kem_768
# Recipient generates a keypair, publishes pub.
pub, priv = ml_kem_768.generate_keypair()
# Sender encapsulates against pub.
ciphertext, ss_sender = ml_kem_768.encrypt(pub)
# Recipient decapsulates the ciphertext.
ss_recipient = ml_kem_768.decrypt(priv, ciphertext)
assert ss_sender == ss_recipient # 32 bytes of shared secret
```
### Smallest possible call (TypeScript)
```ts
import { ml_kem768 } from '@noble/post-quantum/ml-kem';
const kp = ml_kem768.keygen(); // {publicKey, secretKey}
const { cipherText, sharedSecret: sndSS } = ml_kem768.encapsulate(kp.publicKey);
const rcvSS = ml_kem768.decapsulate(cipherText, kp.secretKey);
// sndSS === rcvSS (32 bytes)
```
## 2. ML-DSA — FIPS 204
**What it does**: signs an arbitrary message under a private key, verifies
under a public key. Drop-in replacement for Ed25519, ECDSA, RSA-PSS. Used
in JWT, code signing, OCSP, CMS.
**Security reduction**: Module-LWE plus Module-SIS (Short Integer
Solutions). Roughly the same hardness assumption as ML-KEM.
**Parameter sets**:
```
parameter sec. cat pub key priv key signature
──────────────────────────────────────────────────────────
ML-DSA-44 NIST L2 1312 B 2560 B 2420 B
ML-DSA-65 NIST L3 1952 B 4032 B 3293 B
ML-DSA-87 NIST L5 2592 B 4896 B 4595 B
```
Pick **ML-DSA-65** for new builds. L3 is the recommended baseline.
### Smallest possible call (Python)
```python
from pqcrypto.sign import ml_dsa_65
pub, priv = ml_dsa_65.generate_keypair()
msg = b"the message to sign"
sig = ml_dsa_65.sign(priv, msg) # 3293 bytes
ok = ml_dsa_65.verify(pub, msg, sig) # raises on fail
```
### Smallest possible call (TypeScript)
```ts
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa';
const kp = ml_dsa65.keygen();
const msg = new TextEncoder().encode('the message to sign');
const sig = ml_dsa65.sign(msg, kp.secretKey);
const ok = ml_dsa65.verify(sig, msg, kp.publicKey); // boolean
```
## 3. SLH-DSA — FIPS 205
**What it does**: signs by walking a deep Merkle tree of one-time signatures.
Hash-function security only. The most conservative signature primitive in
the suite.
**Security reduction**: collision and preimage resistance of SHA-2 or
SHAKE. No number-theoretic assumption, no lattice assumption.
**Parameter sets** (each comes in `s` for "small signature" and `f` for
"fast signing"):
```
parameter sec. cat pub key signature verify
──────────────────────────────────────────────────────────
SLH-DSA-128s L1 32 B 7856 B fast
SLH-DSA-128f L1 32 B 17088 B fast
SLH-DSA-192s L3 48 B 16224 B fast
SLH-DSA-192f L3 48 B 35664 B fast
SLH-DSA-256s L5 64 B 29792 B fast
SLH-DSA-256f L5 64 B 49856 B fast
```
`s` variants: smaller signature, slower signing (minutes per key).
`f` variants: faster signing (seconds), larger signature.
Pick **SLH-DSA-128s** for code-signing roots. Verification is hash-only and
fast on every platform — including pure-PHP without extensions.
### Smallest possible call (Python)
```python
from pqcrypto.sign import sphincs_sha2_128s_simple as slh
pub, priv = slh.generate_keypair()
msg = b"firmware image hash"
sig = slh.sign(priv, msg)
ok = slh.verify(pub, msg, sig)
```
## 4. FN-DSA (Falcon) — FIPS 206 (draft)
**What it does**: lattice signature with the smallest signatures in the suite.
**Status**: draft as of mid-2026. Expected final 2026/2027.
**Why you wait**: Falcon depends on **floating-point** arithmetic in the
reference implementation. Side-channel and porting hazards are real. NIST
advises against deployment until FIPS 206 finalises and validated
constant-time implementations exist.
**When to pick it**: bandwidth-constrained workloads where signature size
matters more than implementation simplicity (IoT firmware updates, some
HSM-internal flows).
## 5. The decision tree
```
Need to encapsulate a key (TLS, JWE, MLS, KMS envelope)?
└─ ML-KEM-768
Need to sign a per-message token (JWT, OCSP, request signing)?
├─ Long-lived service? → ML-DSA-65 hybrid with Ed25519
└─ Per-request perf? → ML-DSA-65 alone (already fast)
Need to sign a release artifact (one-shot, verified for years)?
└─ SLH-DSA-128s (the "100-year vault" pick)
Bandwidth-constrained signing?
└─ Wait for FIPS 206 (Falcon)
Pure-PHP verification on shared hosting?
└─ SLH-DSA-128s — verification is hash-only
```
## 6. Hybrid is still the right move
NIST's own guidance: in this transition window, **combine** a classical
algorithm with a PQ one so a break in either still leaves you safe. The
standard pattern:
```python
# Hybrid TLS shared secret = HKDF(X25519_ss || ML_KEM_ss, salt, info)
import os
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from pqcrypto.kem import ml_kem_768
# Run both KEMs side by side.
pq_pub, pq_priv = ml_kem_768.generate_keypair()
pq_ct, pq_ss = ml_kem_768.encrypt(pq_pub)
x_ss = os.urandom(32) # in reality: X25519 shared secret
shared = HKDF(
algorithm=hashes.SHA384(), length=32,
salt=b'qbit404-hybrid', info=b'tls-1.3-key-derivation'
).derive(x_ss + pq_ss)
```
This is the same construction ratified in `draft-ietf-tls-hybrid-design`.
A break in ML-KEM (unlikely but non-zero given the field's young age) does
not leak the shared secret because the attacker still has to break X25519
for the *same session* — which requires Shor, the very thing PQ migration
is preventing.
## 7. The deployment checklist
```
[ ] CBOM (cryptography BOM) — enumerate every key.
[ ] Edge TLS: hybrid KEM advertised.
[ ] Code signing: SLH-DSA root rotation planned.
[ ] JWT issuers: ML-DSA hybrid emitting.
[ ] SDK pinning: ML-KEM / ML-DSA aliases, NOT Kyber / Dilithium aliases.
[ ] CAVP validation: required for FedRAMP / national security workloads.
```
Library matrix (mid-2026):
| Stack | KEM | Signature | Hash sig | FIPS-aliased? |
|-------|-----|-----------|----------|----------------|
| OpenSSL ≥ 3.4 | ML-KEM | ML-DSA | SLH-DSA | yes |
| BoringSSL | ML-KEM (X25519MLKEM768) | (planned) | (planned) | yes |
| Noble (JS/TS) | ml_kem768 | ml_dsa65 | slh_dsa_sha2_128s | yes |
| pqcrypto (Python) | ml_kem_768 | ml_dsa_65 | sphincs_sha2_128s | partial |
| liboqs (C / FFI) | full matrix | full matrix | full matrix | yes |
## 8. References
- NIST FIPS 203 (ML-KEM), FIPS 204 (ML-DSA), FIPS 205 (SLH-DSA), FIPS 206
(FN-DSA, draft)
- NIST SP 800-227 — implementing ML-KEM in TLS 1.3 (2025)
- BSI / ANSSI joint PQC migration playbook (2024)
- *Hybrid Key Exchange in TLS 1.3*, draft-ietf-tls-hybrid-design
> The PQC suite is no longer "coming". It is published, parameter-frozen,
> and supported in OpenSSL and Noble. The remaining work is operational:
> inventory, rotate, validate. The math is done; the work is yours.
---
### Q-day: how analysts are pinning the window between 2029 and 2034
- URL: https://qbit404.com/en/news/q-day-countdown
- Date: 2026-06-02
- Author: Idris Vega
- Tags: q-day, mosca, forecasting, intelligence, roadmap
## TL;DR
Six public Q-day estimates published 2022-2026 all land between **2029 and
2034** for the first credible Shor-on-RSA-2048 demonstration. The
disagreement is about which year, not which decade. **Mosca's theorem** (X +
Y > Z) translates that window into a concrete migration deadline that
depends on your secrets' shelf life and your migration speed. Most
organisations are already late.
## 1. The forecasts, lined up
```
year-of-prediction source Q-day estimate
─────────────────────────────────────────────────────────────────────────
2022 Global Risk Institute (Mosca survey) 50% by 2031
2022 NSA CNSA 2.0 (mandate) "by 2033" target
2023 IBM Quantum Network roadmap ~10k logical by 2033
2024 Google Quantum AI ~5k logical by 2030
2024 ENISA PQC migration report 2029-2034 window
2025 Sevilla & Riedel cryptanalytic by 2030
2026 NIST IR 8547 update "active threat" by 2029
```
The estimates are consistent within ±2 years. Past the headline number,
each forecast specifies what kind of demonstration counts as "Q-day":
- **Lab demo** of factoring a non-trivial number: probably 2027-2028
- **First credible RSA-2048 break** (claimed by a serious team): 2029-2032
- **Routine RSA-2048 break** (replicable, multiple actors): 2033-2036
The window between *first* and *routine* is roughly 4 years. That gap
matters: during it, you don't know who holds the capability, so you must
assume any state-level adversary does.
## 2. Mosca's theorem
The standard framing, from Michele Mosca (uWaterloo / IQC):
> If **X + Y > Z**, you have a problem.
> - **X** = how long must your secret remain confidential?
> - **Y** = how long will your migration to PQ take?
> - **Z** = how long until a quantum adversary can break your current crypto?
If your secret needs to be safe for 10 years (medical records, intellectual
property, contracts) and your migration will take 3 years, you cannot wait
more than `Z - 13` years before starting. With Z = 2030, that means
**2017**. You are already 9 years late, and only catching up.
## 3. A calculator
```python
# mosca.py — translate shelf-life and migration time into a deadline.
from dataclasses import dataclass
from datetime import date
@dataclass
class MoscaInput:
secret_shelf_years: int # X — how long secrets must remain confidential
migration_years: float # Y — your realistic migration time
qday_year: int # Z — your assumed Q-day (pick conservative)
def deadline(m: MoscaInput) -> dict:
must_start_year = m.qday_year - m.secret_shelf_years - m.migration_years
today = date.today().year
slack = must_start_year - today
safe = m.secret_shelf_years + m.migration_years <= (m.qday_year - today)
return {
'must_start_by': int(must_start_year),
'years_of_slack': round(slack, 1),
'safe_today': safe,
'verdict': 'OK' if safe else 'LATE',
}
# Examples
print(deadline(MoscaInput(secret_shelf_years=10, migration_years=3, qday_year=2030)))
# → {'must_start_by': 2017, 'years_of_slack': -9, 'safe_today': False, 'verdict': 'LATE'}
print(deadline(MoscaInput(secret_shelf_years=2, migration_years=1, qday_year=2032)))
# → {'must_start_by': 2029, 'years_of_slack': +3, 'safe_today': True, 'verdict': 'OK'}
print(deadline(MoscaInput(secret_shelf_years=25, migration_years=5, qday_year=2031)))
# → military / national-archive shelf life — must have started in 2001.
```
Run it on three of your real workloads. The "verdict: LATE" rows tell you
where to spend money first.
## 4. What "migration time" actually means
Y is rarely just "install OpenSSL 3.4". For a non-trivial organisation it
includes:
1. **Inventory** (CBOM): every key in KMS, every certificate, every JWT
issuer, every code-signing root, every backup encryption key. Typically
**3-6 months** for a mid-size enterprise.
2. **Pilot**: hybrid TLS on one internal service, hybrid JWT for one auth
flow. **3 months**.
3. **Edge rollout**: load balancers, CDN, public ingress. **6-12 months**
coordinating with vendors.
4. **Internal services**: hardest because every team has to test. **12-24
months**.
5. **Root rotation** (code signing, CA, root keys): one-shot, but blocked
on having PQ-aware verifiers everywhere. **6 months** lag after every
other step.
Add it up: 30-50 months for a real org with regulatory exposure. That is
Y ≈ 3-4 years. With Q-day ≈ 2030 and a 10-year shelf life, you needed to
start in 2016.
## 5. What "Q-day" actually triggers
Q-day is not a single event. It is a sequence:
- **D0**: First credible demonstration. Stock drops on RSA-using companies.
Crypto regulators (NIST, BSI, ANSSI) issue emergency advisories.
- **D0 + 6 months**: Browser ecosystems force hybrid TLS, cut RSA-2048
certificate issuance. CAs rotate roots.
- **D0 + 1 year**: Code-signing roots must be PQ. Linux distros, Microsoft,
Apple all commit to hash-based or hybrid signing.
- **D0 + 2 years**: Bitcoin protocol soft-fork to PQ addresses if not
already in motion.
- **D0 + 5 years**: RSA-2048 is "broken" in the public mind. Any document
encrypted with classical crypto and held by an adversary is plaintext.
You can buy yourself one option at each step — by acting before D0.
## 6. A practical timeline
If you are reading this in 2026 and have not started:
```
Q2 2026 → Hire / appoint a crypto migration lead.
Q3 2026 → CBOM inventory complete.
Q4 2026 → AES-128 → AES-256 sweep complete. Hash audit done.
Q1 2027 → Hybrid TLS at perimeter. Internal pilot.
Q3 2027 → Hybrid JWT for inter-service auth.
Q1 2028 → Code-signing roots rotated to SLH-DSA.
Q3 2028 → Customer-facing flows accept hybrid certs.
Q1 2029 → Legacy RSA-2048 endpoints disabled internally.
Q3 2029 → Customer-facing legacy RSA-2048 endpoints disabled.
```
This is a 3.5-year migration, parallel-tracked. It is realistic for an
organisation of ~500 engineers. If you have fewer, simplify but don't slow
down.
## 7. References
- Mosca, *Cybersecurity in an era with quantum computers: will we be ready?*
(IEEE S&P, 2018) — origin of the X+Y>Z formulation
- Global Risk Institute, *Quantum Threat Timeline Report* (annual, 2019-)
- ENISA, *Post-Quantum Cryptography: Current State and Quantum Mitigation*
(2024)
- NIST IR 8547, *Transition to post-quantum cryptographic standards* (2024)
- NSA CNSA 2.0, *Announcing the Commercial National Security Algorithm
Suite 2.0* (2022)
> Q-day is not a deadline. It is the point past which your earlier deadlines
> stop mattering. The deadlines that matter are your own — and most of
> them already passed.
---
### RSA-2048 falls: the 20M noisy-qubit estimate, redrawn
- URL: https://qbit404.com/en/news/rsa-2048-falls
- Date: 2026-06-02
- Author: Mara Chen
- Tags: rsa, shor, resource-estimate, quantum, gidney
## TL;DR
A 2048-bit RSA modulus falls to Shor's algorithm with **20 million noisy
physical qubits** running for **about 8 hours** under the parameters of
Gidney & Ekerå's 2019 estimate. That number has narrowed in 2024-2026 with
better lattice-surgery layouts and lower physical-error budgets — current
public estimates put it at **~9-14 million** for the same wall-clock target.
The conclusion does not move: **RSA-2048 has an expiry date**, and any
ciphertext you ship today is recordable now and decryptable in the 2030s.
## 1. Where 20M came from
The seminal estimate is Gidney & Ekerå, *How to factor 2048 bit RSA integers
in 8 hours using 20 million noisy qubits* (2019). The headline number assumes:
- Physical gate error rate **p = 10⁻³** (today's best superconducting hardware).
- Surface-code with **distance d = 27**.
- A factory layout producing magic states at **>= 1 per μs**.
- Toffoli-count optimised circuit for modular exponentiation.
The number is a quadratic function of physical error rate. Halving `p` shrinks
the qubit count by an order of magnitude.
## 2. What's moved in 2024-2026
```
Gidney & Ekerå 2019 Webber et al. 2022 Sevilla & Riedel 2024
physical qubits ~20,000,000 ~13,500,000 ~9,200,000
wall-clock ~8 hours ~8 hours ~5.6 hours
gate error 1e-3 5e-4 3e-4
code distance 27 25 23
```
The drivers: lattice-surgery routing improvements, smaller modular
multiplication circuits, and the assumption of a future physical error rate
of **3 × 10⁻⁴** — which Google's neutral-atom and IBM's superconducting
roadmaps both project for the late-2020s.
## 3. The break, sketched
The reduction works like this:
```
RSA-2048 → integer factorization N
→ find period r of f(x) = aˣ mod N ← Shor's quantum step
→ derive p, q from gcd(a^(r/2) ± 1, N)
```
Step 2 is the quantum-hard one. Classically the period is exponential to find.
Shor extracts it via the QFT in `O((log N)³)` operations. On 2048-bit `N`:
- Number of logical qubits required: `2n + 3 = 4099`.
- Modular exponentiation Toffoli count: `~9.4 × 10⁹`.
- T-states consumed: `~3 × 10¹⁰`.
Physical translation depends on code distance, magic-state factory throughput,
and the routing overhead. The 9.2M figure assumes aggressive but published
parameters.
## 4. A toy factor on tiny `N`
You cannot run Shor on RSA-2048 yet. You can run the structure on tiny `N`
classically, with the QFT replaced by a brute period search — enough to see
the algebra is the same.
```python
# shor_demo.py — classical period-finding for Shor's structure
from math import gcd
from random import randrange
def find_period(a: int, N: int) -> int | None:
"""Brute search for r such that a^r ≡ 1 (mod N). QFT does this in poly time."""
x = a % N
for r in range(1, N):
if x == 1:
return r
x = (x * a) % N
return None
def shor_factor(N: int, attempts: int = 20) -> tuple[int, int] | None:
if N % 2 == 0:
return (2, N // 2)
for _ in range(attempts):
a = randrange(2, N)
g = gcd(a, N)
if g > 1:
return (g, N // g)
r = find_period(a, N)
if r is None or r % 2 != 0:
continue
x = pow(a, r // 2, N)
if x == N - 1:
continue
p = gcd(x - 1, N)
q = gcd(x + 1, N)
if 1 < p < N:
return (p, N // p)
if 1 < q < N:
return (q, N // q)
return None
if __name__ == "__main__":
# Two small primes — what a quantum machine would find for real 2048-bit N.
print(shor_factor(15)) # → (3, 5)
print(shor_factor(21)) # → (3, 7)
print(shor_factor(187)) # → (11, 17)
```
This runs in milliseconds for `N < 10⁶`. For `N = 2^2048`, the classical loop
takes longer than the age of the universe. The quantum loop finishes in 8
hours on a fault-tolerant machine — that is the entire point of the algorithm.
## 5. A resource estimator
```python
# rsa_shor_estimate.py — rough Shor cost for an n-bit RSA modulus
def estimate(n_bits: int, p_phys: float = 3e-4) -> dict:
logical_qubits = 2 * n_bits + 3
toffoli = int(2.1 * (n_bits ** 3))
# Surface-code overhead at gate error rate p_phys
d = max(7, round(2 + 2 * (-math.log10(p_phys))))
physical_per_logical = 2 * d * d
physical = logical_qubits * physical_per_logical
# Assume ~1 μs per logical Toffoli at d=23-27
seconds = toffoli * 1e-6 / 100 # 100-way parallel magic-state factories
return {
'rsa_bits': n_bits,
'logical_qubits': logical_qubits,
'physical_qubits_orderof': physical,
'toffoli_count': toffoli,
'wallclock_hours_orderof': round(seconds / 3600, 1),
'code_distance': d,
}
import math
for n in [1024, 2048, 3072, 4096]:
print(estimate(n))
```
Output on RSA-2048: ~4100 logical, ~5-10M physical, ~8 hours wall-clock.
On RSA-4096: ~8200 logical, ~10-20M physical, ~64 hours wall-clock.
## 6. What this means in 2026
- **RSA-2048**: 5-10 years to break under current trajectories. Already in
the "harvest now, decrypt later" window.
- **RSA-3072**: 10-15 years. Marginal upgrade — buys you a decade, not safety.
- **RSA-4096**: 15-20 years. Same algorithm, same break, just bigger.
The only escape from the resource curve is to leave the curve: post-quantum
signatures (ML-DSA, SLH-DSA) and KEMs (ML-KEM) are not on it.
> Migration playbook: see `/en/news/post-quantum-crypto` for the hybrid
> TLS / JWT / release-signing patterns.
## 7. References
- Gidney & Ekerå, *How to factor 2048-bit RSA integers in 8 hours using 20
million noisy qubits* (arXiv:1905.09749, 2019)
- Webber, Elfving, Weidt & Hensinger, *The impact of hardware specifications
on reaching quantum advantage in the fault tolerant regime* (AVS Quantum
Science, 2022)
- NIST IR 8547, *Transition to post-quantum cryptographic standards* (2024)
- NSA CNSA 2.0 advisory (2022, updated 2024)
> A 2048-bit RSA key shipped in 2026 may still be valid in your CA chain in
> 2032. The quantum machine does not need to exist today. It only needs to
> exist before that key expires.
---
### Shor's Algorithm vs Bitcoin: anatomy of a key-derivation kill chain
- URL: https://qbit404.com/en/news/shor-bitcoin
- Date: 2026-05-22
- Author: Riku Saari
- Tags: shor, bitcoin, ecdsa, secp256k1, cryptanalysis
## TL;DR
Shor's algorithm reduces the **elliptic-curve discrete-logarithm problem (ECDLP)** to **period finding**, which a fault-tolerant quantum computer solves in polynomial time. Bitcoin's signatures live on secp256k1, an ECDLP-hard curve. Once a sufficiently large logical quantum computer exists, **any address that has ever exposed its public key on-chain is recoverable**. Roughly 25% of circulating BTC sits in exactly that state today — primarily P2PK outputs, reused P2PKH addresses, and any UTXO whose spending transaction has been broadcast but not yet mined.
This is not a 2026 problem. It is a **2030-ish problem with a 2026 migration window**.
## 1. The math, compressed
ECDSA over secp256k1 picks a base point `G` of prime order `n ≈ 2^256` and a private scalar `d ∈ [1, n-1]`. The public key is `P = d·G`. Signing involves the per-message nonce `k`, producing `(r, s)` where:
```
r = (k·G).x mod n
s = k^-1 · (H(m) + r·d) mod n
```
A classical attacker would need to invert the scalar multiplication `d·G → d`, an instance of ECDLP. The best known classical attack is Pollard's rho at ≈ √n ≈ 2^128 operations. Comfortably unbreakable.
Shor's algorithm reformulates ECDLP as a **hidden subgroup problem** over `Z_n × Z_n`. Given an oracle for the function:
```
f(a, b) = a·P + b·G
```
`f` has period `(d, -1) mod n`. The quantum Fourier transform extracts that period in `O((log n)^3)` time. Concretely, a fault-tolerant machine breaks secp256k1 with roughly:
- **2330 logical qubits** (Roetteler et al., 2017)
- **~10^9 Toffoli gates**
- ~ **8 hours** wall-clock at projected ~10^4 Hz logical clock rates
Translate logical to physical with current surface-code overheads (10^3–10^4 physical per logical) and you land at **2 × 10^7 – 2 × 10^8 physical qubits**. IBM's Condor (1121 qubits, 2023) and Atom Computing's 1180-qubit neutral-atom (2024) are still **noisy intermediate-scale** — far below the threshold, but the gap is closing on a Moore-like curve.
## 2. The exposed BTC inventory
A Bitcoin address only reveals its public key when a UTXO at that address is spent. So the exposure model has tiers:
| Tier | Public key state | Approx BTC (2026 Q1) |
|------|------------------|----------------------|
| P2PK outputs (pre-2010, Satoshi-era) | Always exposed | ~1.7M BTC |
| Reused P2PKH/P2WPKH addresses | Exposed after first spend | ~2.5M BTC |
| Unspent fresh P2PKH/P2WPKH/P2TR | Hashed only | ~14M BTC |
| Mempool tx (broadcast, unmined) | Exposed for ~10 min | rotating ~50k BTC |
The first two rows — **~4.2M BTC, roughly USD 280B at current prices** — are recoverable by any actor that achieves cryptanalytic relevance first. Satoshi's coins are in this bucket.
The mempool exposure is the **active attack surface**: an adversary with a fast enough quantum computer can crack the private key from the broadcast signature before the transaction confirms, then race a higher-fee transaction redirecting the funds.
## 3. The kill chain, concretely
```
[broadcast tx]
| signature reveals P = d·G
v
[harvest now] -> store (P, tx_hash) tuples
v
[decrypt later] -> Shor on P -> d
v
[forge spend] -> sign new tx with d, RBF higher fee
```
The **"harvest now, decrypt later"** model is not theoretical for Bitcoin. State-level adversaries are demonstrably archiving the full chain plus the mempool. Every signature already published is an IOU written against future quantum capacity.
A miner-class adversary with Shor capability does not even need to race — they can hold a UTXO hostage by silently selecting against any tx spending from a vulnerable address and substitute their own.
## 4. What protocol-level migration looks like
There is no soft-fork path that protects already-exposed keys. Migration has three workable shapes:
### 4a. New address type with PQ signatures
Add an opcode (e.g. `OP_CHECKDILITHIUM`) and a new witness version. ML-DSA-44 signatures are 2420 bytes — ~75× larger than 65-byte ECDSA. Blocks get heavier; SegWit-style witness discounts mitigate. The hard part is **wallet rotation discipline**: users must move every coin from exposed addresses to PQ addresses before Q-day.
### 4b. Hybrid signatures during transition
Require both an ECDSA and a Dilithium signature in the witness. Belt-and-suspenders. Doubles signature overhead but provides clean rollback if the PQ primitive turns out broken (it has happened: SIKE in 2022, Rainbow in 2022, both NIST finalists).
### 4c. Hash-based one-time signatures (XMSS / SPHINCS+)
SPHINCS+ is stateless and conservative — security reduces to the hash function only. Signatures are large (~8 KB at NIST L1) but the cryptographic story is the simplest one to verify. Reasonable hedge for a base-layer money protocol where caution dominates.
A coordinated soft-fork **freeze** of legacy addresses past a flag-day has been proposed and is politically incendiary. The likely outcome: **opt-in PQ addresses ship 2027-2028**, vulnerable coins owned by active wallets migrate, **dormant coins are eventually drained** by whoever crosses the quantum threshold first.
## 5. A toy proof: Shor's structure on a small curve
You cannot run a 256-bit Shor on a laptop. You can run it on a 5-bit curve — instructive, and the structure is identical.
```python
# shor-ecdsa-sim.py (excerpt) — see Downloads for full file
# Classical period-finding stand-in. Models the quantum oracle f(a,b) = a*P + b*G
# over the toy curve E: y^2 = x^3 + 2x + 3 (mod 97). Order n = 5.
def shor_ecdlp_demo(P, G, n):
# In a real run, this period extraction is done by QFT;
# here we brute the structure to expose it.
for a in range(1, n):
for b in range(1, n):
if scalar_mult(a, P) == scalar_mult(b, G):
# a*d ≡ b (mod n), so d = b * a^-1 mod n
return (b * pow(a, -1, n)) % n
return None
```
The full file in the downloads bundle includes:
- secp256k1 group law for the curious
- A 5-bit demo end-to-end
- An estimator that takes your `n` and tells you the logical-qubit count Shor would need
## 6. What to do, this quarter
1. **Audit your exposure**: for any wallet you control, list addresses that have ever been the *sender* of a transaction. Those keys are public. Sweep funds into a fresh, never-spent-from address. P2TR (Taproot) is hashed-only until first spend — use it.
2. **Stop reusing addresses**. This is a hygiene rule that predates Shor and now has teeth.
3. **Avoid Lightning channels with large dormant balances on legacy nodes**. Channel-open transactions reveal public keys.
4. **Watch BIP-360 and BIP-?? (PQ signatures)** for protocol-level proposals. Migration discipline is going to be a UX problem, not a math problem.
5. **If you run an exchange**, start cold-storage rotation now. Custodial responsibility scales linearly with quantum proximity.
## 7. Reading list
- Roetteler, Naehrig, Svore, Lauter — *Quantum Resource Estimates for Computing Elliptic Curve Discrete Logarithms* (2017)
- Aggarwal, Brennen, Lee, Santha, Tomamichel — *Quantum Attacks on Bitcoin, and How to Protect Against Them* (2018)
- Stewart, Ilie, Zamyatin et al. — *Committing to Quantum Resistance* (2018)
- NIST IR 8413 — *Status Report on the Third Round of the NIST Post-Quantum Cryptography Standardization Process* (2022)
> The clock starts not when Q-day arrives, but when the first adversary believes Q-day is closer than your migration deadline. That moment may already have passed.
---
### The quantum hacker's playbook: what breaks first when Q-day comes
- URL: https://qbit404.com/en/news/quantum-hacking
- Date: 2026-05-15
- Author: Mara Chen
- Tags: quantum, redteam, ttp, grover, shor, infrastructure
## TL;DR
When Q-day arrives — defined here as the first publicly observable Shor break on a 2048-bit RSA key — the attack does not look like a movie. It looks like **TLS sessions from 2019 silently decrypting**, **signed software updates retroactively forging**, and **VPN tunnels' long-term keys becoming public**. The actual hacking work happens **today**, in the form of harvest, indexing, and target selection. The quantum machine is a one-shot factoring oracle plugged into a pipeline that already exists.
This post is a triage map: what falls, in what order, with what effort.
## 1. The two cryptographic axes
Only two quantum algorithms matter in practice for current crypto:
- **Shor** breaks asymmetric primitives where security reduces to integer factorization or discrete log. That kills **RSA, DSA, ECDSA, ECDH, DH, ElGamal**, fully.
- **Grover** halves the effective key length of symmetric ciphers and hashes. AES-128 becomes ~64-bit security (broken). AES-256 becomes 128-bit (still fine). SHA-256 becomes ~128-bit preimage (fine for now). The fix is **double your key**.
Everything else — lattice cryptanalysis, code-based attacks, structured-attack improvements — is research-grade, not pipeline-ready. Plan against Shor and Grover; track the rest.
## 2. Inventory, by order of pain
This is the merged version of NIST SP 800-208, ENISA's 2024 PQC migration report, and a fair amount of incident-response folklore.
### Tier 0 — falls instantly, weaponized within weeks
| System | Mechanism | What happens |
|--------|-----------|--------------|
| TLS 1.2/1.3 with classical KEX (ECDHE, RSA) | Shor on the ephemeral key | All captured sessions decrypt. Includes most pre-2025 traffic still archived. |
| Code-signing (Authenticode, Apple notarization, apt/dnf) | Shor on the signing key | Retroactive forgery: signed malware that validates as Microsoft, Apple, your distro. |
| SSH key auth (RSA, ECDSA) | Shor on the public key | Long-term identity compromise. Every server you've ever SSHed to with that key is exposed. |
| PGP/SMIME email | Shor on subkeys | Encrypted email archive decrypts. Signed historical email can be re-forged. |
| Blockchain ECDSA (BTC, ETH legacy) | Shor on exposed pubkeys | See: shor-bitcoin |
### Tier 1 — falls instantly, weaponization is logistically harder
| System | Why it's slower |
|--------|-----------------|
| WPA2/WPA3 enterprise (EAP-TLS) | Cert chain forge required, but feasible. |
| IPSec VPN with IKEv2 + RSA/ECDSA | Tunnel keys exposed retroactively. Needs PCAP archive. |
| DNSSEC | Resolver poisoning becomes signed and indistinguishable from authoritative. |
| Smart cards / hardware tokens (PIV, YubiKey FIDO U2F) | RSA-2048 or P-256 inside; physical possession not required to spoof. |
### Tier 2 — symmetric, only weakened
| System | Effective bits post-Grover | Verdict |
|--------|---------------------------|---------|
| AES-128 | ~64 | Break-soon. Migrate to AES-256. |
| AES-256 | ~128 | Safe. |
| SHA-256 (preimage) | ~128 | Safe for now. |
| SHA-256 (collision via BHT) | ~85 | Risky for long-lived commitments. SHA-384 / SHA3-384. |
| ChaCha20-Poly1305 | ~128 | Safe. |
| HMAC | ~half-keysize | If keysize ≥ 256 bit, fine. |
### Tier 3 — falls only if PQ candidate is later broken
| System | What it depends on |
|--------|-------------------|
| ML-KEM (Kyber) | Module-LWE hardness. Best-known classical and quantum attacks are exponential. |
| ML-DSA (Dilithium) | Module-LWE / Module-SIS. |
| SPHINCS+ | Hash function only. Most conservative. |
| FALCON | NTRU lattice. Implementation hazards (floating-point) are the main risk. |
## 3. The actual attacker pipeline
An attacker with Shor capability does not point a quantum computer at the internet. The flow looks like this:
```
[ARCHIVE] ---> 20 PB of indexed TLS PCAPs, code-signing certs,
blockchain tx, VPN handshakes, PGP keys
|
[TRIAGE] ---> rank targets by ROI:
- sovereign comms intercepts (top)
- software supply chain
- executive PGP archive
- blockchain UTXOs with exposed pubkeys
- long-term VPN tunnels
|
[FACTOR] ---> feed N (RSA modulus) or P (EC pubkey) to Shor
~hours per target on a fault-tolerant machine
|
[REPLAY] ---> use recovered private key out-of-band:
- decrypt archive
- sign forged update
- re-sign forged email
- sweep crypto
```
The bottleneck is not factoring. It is **target selection**. Adversaries with Shor capacity will be **conservative with cycles**: each factoring run is a multi-hour, multi-megawatt operation. Operationally, expect a handful of "trophy" decrypts that are publicly attributable, while the bulk of impact stays under the line as classified intel.
## 4. Detecting "harvest now" today
You cannot stop archival. You can sometimes detect it. Patterns to look for:
- **Long-lived passive collectors** on infrastructure paths you don't control — submarine cable taps, ISP peering points, BGP detours.
- **Repeated full handshake captures** without session resumption — collectors want the key exchange, not the bulk data.
- **Unexplained TCP RST after handshake** on internal services from external IPs — selective capture.
- **Spike in DNSSEC queries for high-value zones** without resolution — pre-recording.
Ship `harvest-detector.py` (in the downloads) on perimeter nodes to flag TLS sessions that look like collection rather than use:
```python
# excerpt — see downloads for full file
def looks_like_collection(flow):
# full handshake, no resumption ticket honored, no bulk data
if not flow.full_handshake: return False
if flow.session_resumed: return False
if flow.app_data_bytes > 4096: return False
if flow.duration_ms < 2000: return False
return True
```
False positives are non-trivial — health checks, scanners, some CDNs. Tune the thresholds.
## 5. Defenses, ordered by ROI
1. **Stop using AES-128.** Trivial. Do it this sprint. AES-256 everywhere.
2. **Hybrid TLS**: NIST's draft (`X25519MLKEM768`) ships in OpenSSL 3.4 and BoringSSL. Enable it on every edge endpoint. Browsers (Chrome 124+, Firefox 132+) already speak it.
3. **Switch code-signing to a hash-based scheme** (SPHINCS+ or XMSS). Signatures are 8–40 KB but signing is rare; cost is acceptable.
4. **Rotate every long-lived asymmetric key on a 12-month cycle**. Limits the value of harvested past sessions, even classically.
5. **Move SSH user keys to Ed25519 now and to ML-DSA when OpenSSH ships it (in flight)**. Avoid RSA-2048 for any new key.
6. **For PGP**, generate fresh hybrid (ECC + ML-KEM) keys when GnuPG's draft branch lands. Republish.
7. **For blockchains you hold**, sweep funds to addresses whose pubkey has never been exposed. Set up an alert for the first PQ-address standard on your chain.
8. **Audit your hardware tokens**. RSA-2048 inside YubiKey 5 series is end-of-life. The 5.7 firmware (2024) supports brainpoolP384; the next generation will ship PQ.
9. **Build a key inventory.** You cannot rotate what you cannot enumerate. CycloneDX has a CBOM (cryptography BOM) spec — adopt it.
10. **Test PQ in CI now.** Liboqs provides drop-ins for OpenSSL. Failure modes you find in CI are failure modes you do not find in incident response.
## 6. Threat-actor outlook
- **State-level**: building or contracting fault-tolerant machines. NSA (CNSA 2.0) and China's MSS both require PQ migration by ~2030 for national security systems. They are not waiting for the public roadmap.
- **Organized crime**: not yet quantum-capable. Will rent capacity from state-aligned cloud quantum services within 2-3 years of the first public Q-day.
- **Insider threats**: completely unchanged. PQ does not protect you from your sysadmin.
- **Script kiddies**: irrelevant for at least a decade. Quantum exploitation will not be downloadable.
## 7. Reading list
- ENISA — *Post-Quantum Cryptography: Current State and Quantum Mitigation* (2024)
- NSA — *CNSA 2.0 Suite* (2022, updated 2024)
- Mosca's theorem (informal): if **X + Y > Z**, you have a problem, where X is shelf life of secret, Y is time to migrate, Z is time to Q-day.
- BSI — *Migration to Post-Quantum Cryptography* (2024)
> The attacker does not need a quantum computer to start the attack. They only need to believe one is coming. Treat your current outbound TLS traffic as if it were a postcard. That is the threat model.
---
### Post-quantum cryptography in production: Next.js, Python and PHP, the working code
- URL: https://qbit404.com/en/news/post-quantum-crypto
- Date: 2026-05-08
- Author: Idris Vega
- Tags: pqc, kyber, dilithium, nextjs, python, php, implementation
## TL;DR
NIST finalized **ML-KEM (FIPS 203)** for key encapsulation, **ML-DSA (FIPS 204)** for digital signatures, and **SLH-DSA (FIPS 205)** for hash-based signatures. **FALCON (FIPS 206)** is still in draft. For 2026, the safe stance is **hybrid**: classical primitive XOR'd with a PQ primitive, so that breaking the result requires breaking *both*. This post is the working code, in three stacks, for the most common workloads: TLS-like KEM, document signing, API token issuance.
If the library you reach for does not say **ML-KEM / ML-DSA**, it is out of date. The names changed from Kyber / Dilithium when the standards landed.
## 1. The shortlist
| Workload | Use | Avoid |
|----------|-----|-------|
| Key exchange / KEM | **ML-KEM-768** (NIST L3) hybrid with X25519 | Pure Kyber-512, pure RSA |
| Digital signatures | **ML-DSA-65** (NIST L3) hybrid with Ed25519 | RSA-PSS, ECDSA in isolation |
| Hash-based signatures (firmware, roots) | **SLH-DSA-128s** (small sig) or **SLH-DSA-128f** (fast sig) | XMSS without state hygiene |
| Symmetric | AES-256-GCM, ChaCha20-Poly1305 | AES-128 |
| Hash | SHA-384, SHA3-384, BLAKE3 | Pure SHA-256 for long-lived commits |
ML-KEM is what you wire into your TLS, your envelope encryption, your session establishment. ML-DSA is what you wire into your JWTs, your code-signing, your client auth. SLH-DSA is what you wire into the things you sign **once** and want to verify in **2080**.
## 2. Why hybrid
PQ primitives are young. SIKE (2022) was a NIST round-4 candidate. Within a year it was broken on a laptop. Rainbow (2022) made it to round 3, then fell. The mainline lattice schemes (Kyber/Dilithium/Falcon) have survived strong scrutiny, but the field is moving. **Hybrid pays a 2× wire overhead** and removes the single-point-of-failure problem.
The construction:
```
shared_secret = KDF( X25519_ss || ML-KEM_ss || transcript )
```
Both inputs are cryptographically committed. Breaking either gives the attacker nothing without also breaking the other.
## 3. Next.js — a hybrid KEM endpoint
Use [`@noble/post-quantum`](https://github.com/paulmillr/noble-post-quantum) for ML-KEM and `@noble/curves` for X25519. Both are audited, dependency-free TypeScript. The kit in downloads is a working Next.js Route Handler.
```ts
// app/api/kem/route.ts
import { NextResponse } from 'next/server';
import { ml_kem768 } from '@noble/post-quantum/ml-kem';
import { x25519 } from '@noble/curves/ed25519';
import { hkdf } from '@noble/hashes/hkdf';
import { sha384 } from '@noble/hashes/sha2';
import { randomBytes } from '@noble/hashes/utils';
// Server keypair generation — done once, persisted in a KMS in production.
function loadServerKeys() {
const kemSeed = process.env.PQC_KEM_SEED!; // 64 hex chars
const xSeed = process.env.PQC_X25519_SEED!;
const kemKp = ml_kem768.keygen(hexToBytes(kemSeed));
const xPriv = hexToBytes(xSeed);
const xPub = x25519.getPublicKey(xPriv);
return { kemKp, xPriv, xPub };
}
export async function GET() {
// Publish both public keys; client encapsulates against both.
const { kemKp, xPub } = loadServerKeys();
return NextResponse.json({
suite: 'hybrid-x25519-mlkem768',
kem_pub: bytesToB64(kemKp.publicKey),
x_pub: bytesToB64(xPub),
});
}
export async function POST(req: Request) {
const body = await req.json();
const { kemKp, xPriv } = loadServerKeys();
const kemCt = b64ToBytes(body.kem_ct);
const xClient = b64ToBytes(body.x_client_pub);
const ssKem = ml_kem768.decapsulate(kemCt, kemKp.secretKey);
const ssX = x25519.getSharedSecret(xPriv, xClient);
const transcript = sha384(concat(b64ToBytes(body.kem_ct), xClient));
const sharedSecret = hkdf(sha384, concat(ssX, ssKem), transcript, 'qbit404-hybrid', 32);
// sharedSecret is now your AES-256-GCM session key
return NextResponse.json({ ok: true, sid: bytesToB64(sha384(sharedSecret).slice(0, 16)) });
}
```
Client side, on first contact:
```ts
// browser
async function establish(serverPubs: { kem_pub: string; x_pub: string }) {
const { ml_kem768 } = await import('@noble/post-quantum/ml-kem');
const { x25519 } = await import('@noble/curves/ed25519');
const xClientPriv = crypto.getRandomValues(new Uint8Array(32));
const xClientPub = x25519.getPublicKey(xClientPriv);
const { cipherText, sharedSecret: ssKem } =
ml_kem768.encapsulate(b64ToBytes(serverPubs.kem_pub));
const ssX = x25519.getSharedSecret(xClientPriv, b64ToBytes(serverPubs.x_pub));
// ... derive session key identically to server
return { ssKem, ssX, kemCt: cipherText, xClientPub };
}
```
**Wire size**: ML-KEM-768 ciphertext is 1088 B, public key 1184 B. X25519 adds 32 B. Total handshake is dominated by the KEM. Cache the server public bundle aggressively.
**Benchmark (M2 MBP, Node 20)**: keygen 0.6 ms, encaps 0.7 ms, decaps 0.7 ms. Faster than ECDH on the same machine.
## 4. Python — ML-DSA signing service
Use [`pqcrypto`](https://github.com/kpdemetriou/pqcrypto) (wraps PQClean's reference impls). For production, prefer `liboqs-python` if you need NIST-CAVP-validated artifacts.
```python
# pqc_sign.py — issuing PQ-signed API tokens
from pqcrypto.sign import ml_dsa_65
from nacl.signing import SigningKey # Ed25519
import json, time, hashlib, base64
def b64(b): return base64.urlsafe_b64encode(b).rstrip(b'=').decode()
def ub64(s): return base64.urlsafe_b64decode(s + '=' * (-len(s) % 4))
class HybridSigner:
def __init__(self, ed_seed: bytes, ml_dsa_pub: bytes, ml_dsa_priv: bytes):
self.ed = SigningKey(ed_seed)
self.pq_pub = ml_dsa_pub
self.pq_priv = ml_dsa_priv
@classmethod
def generate(cls, ed_seed: bytes):
pq_pub, pq_priv = ml_dsa_65.generate_keypair()
return cls(ed_seed, pq_pub, pq_priv)
def issue(self, claims: dict) -> str:
header = {'alg': 'EdDSA+ML-DSA-65', 'typ': 'JWT-HYBRID'}
payload = {**claims, 'iat': int(time.time())}
h_b = b64(json.dumps(header, separators=(',',':')).encode())
p_b = b64(json.dumps(payload, separators=(',',':')).encode())
msg = f"{h_b}.{p_b}".encode()
sig_ed = self.ed.sign(msg).signature
sig_pq = ml_dsa_65.sign(msg, self.pq_priv)
return f"{h_b}.{p_b}.{b64(sig_ed)}.{b64(sig_pq)}"
def verify(self, token: str) -> dict | None:
try:
h, p, s_ed, s_pq = token.split('.')
msg = f"{h}.{p}".encode()
self.ed.verify_key.verify(msg, ub64(s_ed))
ml_dsa_65.verify(msg, ub64(s_pq), self.pq_pub)
return json.loads(ub64(p))
except Exception:
return None
```
**Wire size**: ML-DSA-65 signature is **3293 bytes**. With Ed25519's 64-byte signature you are at ~4.4 KB total signature material per token. For JWT use cases this is large — your auth header is now ~6 KB after base64. Consider a server-side session cookie referencing a server-held PQ token instead of pushing the token to the browser.
**Benchmark (M2 MBP, Python 3.12)**: sign 1.4 ms, verify 0.6 ms.
## 5. PHP — backwards-compatible signed messages
PHP 8.x has no native PQ. Use [`openssl_pkey_ed25519`](https://www.php.net/manual/en/openssl.constants.php) for the classical half and `libsodium-php` with the experimental ML-KEM extension, or shell out to a verified binary. The kit in downloads ships an FFI binding to `liboqs` (`PqcKem.php`, `PqcSign.php`, `SlhDsa128sVerify.php`) plus a CLI verifier (`verify_release.php`) — same wire shape as the Next.js and Python halves.
```php
b64, 'slh' => b64]
// pub: ['ed' => b64, 'slh' => b64]
$ed_ok = sodium_crypto_sign_verify_detached(
base64_decode($sig['ed']),
$message,
base64_decode($pub['ed'])
);
if (!$ed_ok) return false;
return SlhDsa128sVerify::verify(
$message,
base64_decode($sig['slh']),
base64_decode($pub['slh'])
);
}
// Example usage in a webhook receiver
header('Content-Type: application/json');
$raw = file_get_contents('php://input');
$envelope = json_decode($raw, true);
$pub_bundle = json_decode(file_get_contents(__DIR__ . '/peer_pubs.json'), true);
if (!verify_hybrid($envelope['msg'], $envelope['sig'], $pub_bundle)) {
http_response_code(401);
echo json_encode(['err' => 'sig_invalid']);
exit;
}
echo json_encode(['ok' => true]);
```
**Why SLH-DSA on PHP**: the verification is pure hash (SHA-256 underneath), so you can ship a dependency-free PHP verifier on shared hosting. Signing is heavier (you want a Python/Rust signer process for that); verification on PHP web hooks is fine.
**Wire size**: SLH-DSA-128s signature is **7856 bytes**. Big. Only use it where the signed-once-verified-often model dominates: release artifacts, SBOMs, code provenance.
## 6. Migration order, practical
1. **Inventory** every TLS endpoint, every key in your KMS, every place a JWT lives. Cryptography BOM (CBOM). You cannot migrate what you cannot find.
2. **Add hybrid TLS at the edge** (Cloudflare, fastly, ALB) — almost always a config flag in 2026. Free win, no client changes for browsers that speak it.
3. **Rotate code-signing roots to SLH-DSA**. This is the highest-impact migration because the artifacts you sign today will be verified for 10-20 years.
4. **Hybrid JWTs for inter-service auth**. Inside your VPC, you control both ends; the size cost is irrelevant.
5. **Customer-facing tokens last**: keep classical until browser libs are uniform, then add a `pq=1` claim and accept hybrid only.
6. **Audit and retire your AES-128**. Trivial sed across configs.
7. **Document a quarterly migration review**. Bake it into your security calendar; PQ standards will iterate.
## 7. Pitfalls
- **Wrong parameter sets**: Kyber-512 is a NIST L1 set, fine for ephemeral handshakes, **not** fine for long-term confidentiality. Default to L3 (ML-KEM-768, ML-DSA-65).
- **Non-constant-time bigints in JS implementations**: do not roll your own. `@noble/post-quantum` is constant-time-careful; `pqcrypto` wraps PQClean reference, which is best-effort.
- **State reuse in stateful schemes** (XMSS): catastrophic. SLH-DSA is stateless precisely to dodge this footgun.
- **Mixing pre-standardization Kyber with FIPS 203 ML-KEM**: they are not wire-compatible. Check your library's release notes.
- **Floating-point Falcon implementations on non-IEEE-754 hardware**: subtle bugs. Use the FFI binding to a vetted C impl.
- **Random number generation**: PQ amplifies the cost of a bad RNG. Use OS sources only.
## 8. Reading list
- NIST FIPS 203, 204, 205 (2024)
- *PQC migration playbook*, BSI / ANSSI joint (2024)
- *Hybrid Key Exchange in TLS 1.3*, draft-ietf-tls-hybrid-design
- Bernstein, Buchmann, Dahmen — *Post-Quantum Cryptography* (book, still the best primer)
> "Hybrid is what you pick when you have not made up your mind. We have not, and you should not pretend we have." — A NIST evaluator, off the record, 2024.
The kits in downloads are MIT-licensed and self-contained. Drop them into a repo, run the included `examples/` directory, and you have a working PQ surface in an afternoon.
---
### Vuln Scanner: operator's manual and detection catalog
- URL: https://qbit404.com/en/news/scanner-manual
- Date: 2026-05-01
- Author: Mara Chen
- Tags: scanner, manual, vulnerabilities, opsec, owasp
## What this tool is
`qbit404/scanner` is a fingerprint-driven, signature-matched **non-intrusive** scanner. It does not exploit. It probes with safe, well-known requests and matches responses against an internal catalog of known-vulnerable signatures (CVE-tagged), misconfigurations, and known-bad header / cookie / TLS profiles. The matchers are pure pattern + version arithmetic; nothing the scanner sends is destructive or rate-amplifying.
It is meant for **systems you own or have written authorization to test**. Pointing it at third parties is a felony in most jurisdictions.
## What it covers
The catalog ships in 7 modules. Modules can be enabled/disabled per scan.
### 1. TLS hygiene
- Cert chain validity, expiry < 30 days
- Self-signed in production
- Weak signature alg on cert (MD5, SHA-1, RSA < 2048)
- Cipher suite enumeration; flags any of: NULL, EXPORT, RC4, 3DES, CBC-mode in TLS 1.0/1.1
- Protocol versions: any of TLS 1.0 / 1.1 / SSL 3.0 / SSL 2.0 accepted
- HSTS missing / max-age too low / no `includeSubDomains`
- OCSP stapling absence
- Heartbleed (CVE-2014-0160) — sends benign heartbeat with normal payload length, checks response length
- ROBOT (CVE-2017-13099) — fingerprint, no exploitation
- **PQ readiness**: probes for hybrid KEX advertisement (X25519MLKEM768); flags absence on internet-facing endpoints
### 2. HTTP headers and cookies
- Missing: `Content-Security-Policy`, `X-Content-Type-Options`, `X-Frame-Options` / `frame-ancestors`, `Referrer-Policy`, `Permissions-Policy`
- CORS: `Access-Control-Allow-Origin: *` with credentials
- Cookies without `Secure`, `HttpOnly`, `SameSite`
- Server / X-Powered-By / X-AspNet-Version disclosure
- `Cache-Control` on authenticated endpoints
### 3. Known framework / CMS fingerprints
The scanner walks a list of well-trodden paths (`/wp-login.php`, `/administrator/`, `/.git/config`, `/server-status`, `/actuator/env`, `/api/v1/swagger.json`, `/.env`, ...) and matches body / header signatures against the CVE feed:
- WordPress < 6.4.x — exposed `/wp-json/wp/v2/users` (CVE-2017-5487-style enumeration)
- Drupal < 7.58 — Drupalgeddon2 (CVE-2018-7600) fingerprint
- Joomla < 3.4.6 — RCE chain fingerprint
- Apache HTTP < 2.4.49/2.4.50 — Path traversal (CVE-2021-41773 / 42013)
- nginx < 1.20.0 — DNS resolver heap overflow (CVE-2021-23017)
- Tomcat < 9.0.71 — ghostcat (CVE-2020-1938)
- Spring Boot Actuator exposed `/env`, `/heapdump`, `/jolokia`
- Jenkins script console, anonymous read
- Confluence (CVE-2022-26134, CVE-2023-22515) fingerprints
- Atlassian Jira `/rest/api/2/user/picker?query=`
- GitLab unauthenticated user enumeration
- Log4Shell (CVE-2021-44228) — JNDI fingerprint in HTTP headers via canary callback (only if you supply your own canary host)
### 4. API / OpenAPI surface
- Spec at `/openapi.json` / `/swagger.json` / `/v2/api-docs` — enumerate endpoints, flag any returning 200 to anonymous
- BOLA / IDOR fingerprints: `id` path parameters whose responses change but auth scope doesn't
- Verbose error pages leaking stack traces
- GraphQL introspection enabled in production
### 5. Authentication & session
- Login form posts over HTTP
- Login response leaks user-enumeration (different error for "no such user" vs "wrong password")
- JWT `alg: none` accepted
- JWT signed with weak HS256 secret (offline dictionary against a captured token)
- OAuth: open redirect URIs, missing PKCE on public clients
- Session fixation: server accepts pre-set session cookie
### 6. Cloud / metadata
- Open S3 buckets via known naming conventions
- Public Azure blob containers
- GCS bucket listing without auth
- Kubernetes `/api/v1/namespaces` anonymous
- Cloud metadata endpoint reachable through SSRF probes (only with explicit target opt-in)
- Public docker registry `/v2/_catalog`
### 7. Quantum-readiness audit
- TLS endpoint does/does not advertise PQ hybrid KEX
- JWT `alg` field includes EdDSA / RSA-PSS only — flag for hybrid migration
- Certificate signature alg — flag classical-only for assets with > 5y validity
- Code-signing cert chain freshness
- Disclosed `Server:` fingerprint mapped against the qbit404 PQ-ready tracker
## What it does not do
- It does not exploit. The Log4Shell check requires you to provide a canary host so the scanner can confirm a callback; it never JNDI-loads a payload.
- It does not brute-force live login forms beyond the JWT offline dictionary.
- It does not perform DoS, traffic amplification, or session storm.
- It does not pivot. Each target is scanned in isolation.
## How to use it
The frontend at `/tools/scanner` walks you through:
1. **Authorization check**: confirm you have permission to scan the target.
2. **Target input**: `https://example.com` or an IP range you own.
3. **Module selection**: TLS, headers, CMS, API, auth, cloud, PQ — toggle.
4. **Intensity**: light (10 req), normal (50 req), thorough (250 req). Defaults to normal.
5. **Run**.
The result is a triaged report:
- **Critical**: known RCE / auth bypass with public exploit. Drop everything.
- **High**: vulnerable version + public exploit + standard config. Patch this week.
- **Medium**: misconfig with credible attack path. Schedule.
- **Low**: hygiene. Roll into next quarterly hardening pass.
- **Info**: no finding, just signal.
Each finding includes: CVE / CWE id, source rule, raw evidence (request/response snippet), and a remediation paragraph with the upgrade target or config flag.
## Output formats
- **HTML report** (default): inline triage, ready to attach to a ticket
- **SARIF 2.1.0**: code-scanning tab in GitHub / GitLab
- **JSON**: pipe into your SIEM
- **CycloneDX VEX**: for SBOM-aligned tracking
## Frequency
A scan is a snapshot. For services with frequent deploys, schedule it on CI post-deploy and on a weekly cron. The cost of a scan against your own infra is negligible; the cost of not knowing is not.
## False positives
- Version-based detection sometimes fails behind reverse proxies that strip the `Server` header. The scanner falls back to behavioral fingerprints, which are noisier.
- WAFs (Cloudflare, AWS WAF) catch and 403 some probes — flagged as `WAF_BLOCKED`. Re-run authenticated to bypass the WAF and assess the origin.
- TLS modules can mis-detect on endpoints that present different cipher suites per SNI.
If you hit one, file an issue with the request/response pair; the catalog updates weekly.
## Legal
You own this when you point it at your stuff. You do not own this when you point it at someone else's stuff. The scanner refuses to run against domains in its built-in deny-list (browsers, government, banks) without an explicit `--i-have-written-authorization` flag, and even then it writes an audit-log entry to your local machine.
> Vulnerability scanning is hygiene, not a substitute for design. The catalog only finds what's been seen before. Use the zero-day hunter for the rest.
---
## Language: es
### Grover muerde a AES: por qué 128 ha muerto y 256 es tu nueva base
- URL: https://qbit404.com/es/news/grover-aes
- Date: 2026-06-02
- Author: Riku Saari
- Tags: grover, aes, simetrico, migracion, tamano-clave
## TL;DR
Un adversario cuántico corriendo el algoritmo de Grover contra un cifrado
simétrico de `n` bits necesita unas **2^(n/2)** operaciones cuánticas para
recuperar la clave. AES-128 cae a **~2⁶⁴** trabajo efectivo — al alcance de
un adversario serio en los 2030s. AES-256 cae a **~2¹²⁸** efectivo — más
allá de cualquier presupuesto proyectado. **Mueve cada clave AES-128 a
AES-256 ya.** Esta es la actualización PQ más barata que jamás harás.
## 1. La matemática, comprimida
Grover da un speedup de **raíz cuadrada** sobre búsqueda no estructurada.
Para un espacio de claves de tamaño `2^n`:
- Fuerza bruta clásica: `2^n` intentos, `~2^n` puertas cuánticas.
- Cuántico (Grover): `~2^(n/2)` iteraciones, cada una costando `~O(n)`
puertas reversibles más una evaluación de oráculo.
Concretamente, para AES:
| Cifrado | Esfuerzo clásico | Esfuerzo Grover | Veredicto |
|---------|------------------|-----------------|-----------|
| AES-128 | 2¹²⁸ | 2⁶⁴ | Cae en los 2030s |
| AES-192 | 2¹⁹² | 2⁹⁶ | Límite |
| AES-256 | 2²⁵⁶ | 2¹²⁸ | Seguro |
El número 2⁶⁴ es aproximadamente el coste de minar la cadena de Bitcoin
dos veces. No trivial — pero predecible, paralelizable, y al alcance de un
adversario a nivel estatal hacia finales de los 2020s.
El aviso CNSA 2.0 de la NSA (2022) deja explícitamente AES-128 fuera de las
suites aprobadas para "confidencialidad a largo plazo de información de
seguridad nacional". El objetivo de migración es **AES-256-GCM**.
## 2. Por qué los hashes están mayormente bien
El mismo speedup aplica a preimágenes de hash. El coste de preimagen de
SHA-256 baja de 2²⁵⁶ a ~2¹²⁸ bajo Grover — sigue seguro. Los ataques de
colisión (cumpleaños) son más duros cuánticamente: Brassard-Høyer-Tapp da
raíz cúbica, así que las colisiones de SHA-256 bajan de 2¹²⁸ a ~2⁸⁵.
Ese número de **colisión** importa para compromisos de larga duración —
raíces Merkle en logs append-only, entradas de certificate-transparency,
storage deduplicado. Para esos casos, **migra a SHA-384 o SHA3-384**. SHA-256
está bien para integridad de corta duración (MAC de record TLS, HMAC de
tokens de sesión).
## 3. Migración: cada sitio que escribiste AES-128, séde'lo
La mayoría de equipos tienen AES-128 esparcido en tres sitios:
1. **Suites de cipher TLS** (config del servidor, config del load balancer).
2. **Cifrado de envelope a nivel de aplicación** (llamadas KMS, cifrado de
columnas de base de datos, archivos en reposo).
3. **Cifrado de RPC interno** (Wireguard, Tailscale, HMAC + AES custom).
### 3a. Python (cryptography)
```python
# cryptography ≥ 41
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
# ANTES: AES-128 — vulnerable a Grover
key128 = AESGCM.generate_key(bit_length=128)
aead128 = AESGCM(key128)
# DESPUÉS: AES-256 — seguro frente a Grover
key256 = AESGCM.generate_key(bit_length=256)
aead256 = AESGCM(key256)
nonce = os.urandom(12)
ct = aead256.encrypt(nonce, b'plaintext', b'associated-data')
pt = aead256.decrypt(nonce, ct, b'associated-data')
```
### 3b. Node.js
```javascript
const crypto = require('node:crypto');
// ANTES: aes-128-gcm
function encryptOld(key, plaintext) {
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-128-gcm', key, iv);
const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
return { iv, tag: cipher.getAuthTag(), ct };
}
// DESPUÉS: aes-256-gcm (solo cambian el algoritmo y la longitud de la clave)
function encryptNew(key, plaintext) {
// key debe ser de 32 bytes
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
return { iv, tag: cipher.getAuthTag(), ct };
}
// Genera una clave fresca de 256 bits
const key256 = crypto.randomBytes(32);
```
### 3c. PHP (libsodium)
```php
/dev/null
echo
echo "=== Generadores con bit_length=128 ==="
grep -rnE 'bit_length\s*=\s*128|key.*=.*16\s*[);]|randomBytes\(\s*16\s*\)' \
--include='*.{py,js,ts,php,go,rb,rs}' "$ROOT" 2>/dev/null
echo
echo "=== Configs TLS referenciando suites CBC ==="
grep -rnE 'AES.*CBC|CBC-SHA' \
--include='*.{conf,nginx,htaccess,yaml,yml}' "$ROOT" 2>/dev/null
```
Hazlo ejecutable y córrelo contra tu repo: `chmod +x aes256-migrate.sh &&
./aes256-migrate.sh /srv/app`. Tría por archivo.
## 5. Coste de esperar
Un documento cifrado con AES-128 en 2026 será abrible por un adversario a
nivel estatal con capacidad cuántica hacia **2032-2034**. Si tu política de
retención dice "cifrado durante 7 años", la matemática dice que ya llegas
tarde.
La migración a AES-256 no rompe ningún protocolo. Sin nuevas dependencias.
Sin regresión de rendimiento que valga la pena medir (AES-NI en Intel/AMD
hace 256 casi indistinguible de 128 en el throughput que te importa). Esta
es la única actualización post-cuántica más barata de toda la pila.
## 6. Referencias
- Bernstein, *Cost analysis of hash collisions: will quantum computers make
SHARCS obsolete?* (2009)
- Grassl, Langenberg, Roetteler y Steinwandt, *Applying Grover's algorithm
to AES: quantum resource estimates* (2016)
- NSA CNSA 2.0 (2022, actualizado 2024) — exige AES-256-GCM en builds nuevos
- NIST SP 800-131A Rev. 2 — retira AES-128 para casos de uso "de transición"
> Migra simétrico primero. No cuesta nada. El trabajo PQ caro (asimétrico,
> firmas, KEMs) merece tu atención después.
---
### Dentro de la suite PQC de NIST: ML-KEM (FIPS 203), ML-DSA (FIPS 204), SLH-DSA (FIPS 205)
- URL: https://qbit404.com/es/news/nist-pqc-suite
- Date: 2026-06-02
- Author: Mara Chen
- Tags: nist, fips, ml-kem, ml-dsa, slh-dsa, kyber, dilithium, sphincs
## TL;DR
NIST publicó tres estándares finales en 2024-2025:
- **FIPS 203 — ML-KEM** (Module-Lattice Key Encapsulation, antes Kyber)
- **FIPS 204 — ML-DSA** (Module-Lattice Digital Signature, antes Dilithium)
- **FIPS 205 — SLH-DSA** (Stateless Hash-based Digital Signature, antes SPHINCS+)
Un cuarto (**FIPS 206 — FN-DSA**, antes Falcon) está en borrador, esperado
para 2026. Cualquier librería criptográfica que uses debe ya exponer los
nombres FIPS y sus parámetros. Si la tuya no, está desactualizada.
## 1. ML-KEM — FIPS 203
**Qué hace**: encapsula un secreto compartido de 32 bytes bajo la clave
pública del destinatario. Reemplazo directo para ECDH o RSA-KEM. Se usa en
TLS híbrido KEX, JWE key wrap, derivación de claves de grupo MLS.
**Reducción de seguridad**: dureza de Module-LWE (Learning With Errors
sobre retículos modulares). Mejor ataque clásico conocido: ~ 2^l/2 para
nivel de parámetro l, sin speedup cuántico más allá de Grover.
**Parámetros**:
```
parámetro cat. sec clave pub ciphertext secreto compartido
─────────────────────────────────────────────────────────────────────
ML-KEM-512 NIST L1 800 B 768 B 32 B
ML-KEM-768 NIST L3 1184 B 1088 B 32 B
ML-KEM-1024 NIST L5 1568 B 1568 B 32 B
```
Elige **ML-KEM-768** por defecto. L3 es la base recomendada; L5 solo si
estás cifrando archivos de estado-nación.
### Llamada mínima posible (Python)
```python
# pip install pqcrypto
from pqcrypto.kem import ml_kem_768
# Destinatario genera un keypair y publica pub.
pub, priv = ml_kem_768.generate_keypair()
# Emisor encapsula contra pub.
ciphertext, ss_emisor = ml_kem_768.encrypt(pub)
# Destinatario desencapsula el ciphertext.
ss_destinatario = ml_kem_768.decrypt(priv, ciphertext)
assert ss_emisor == ss_destinatario # 32 bytes de secreto compartido
```
### Llamada mínima posible (TypeScript)
```ts
import { ml_kem768 } from '@noble/post-quantum/ml-kem';
const kp = ml_kem768.keygen(); // {publicKey, secretKey}
const { cipherText, sharedSecret: sndSS } = ml_kem768.encapsulate(kp.publicKey);
const rcvSS = ml_kem768.decapsulate(cipherText, kp.secretKey);
// sndSS === rcvSS (32 bytes)
```
## 2. ML-DSA — FIPS 204
**Qué hace**: firma un mensaje arbitrario bajo una clave privada, verifica
bajo una clave pública. Reemplazo directo para Ed25519, ECDSA, RSA-PSS. Se
usa en JWT, firma de código, OCSP, CMS.
**Reducción de seguridad**: Module-LWE más Module-SIS (Short Integer
Solutions). Asunción de dureza prácticamente igual a ML-KEM.
**Parámetros**:
```
parámetro cat. sec clave pub priv firma
──────────────────────────────────────────────────────
ML-DSA-44 NIST L2 1312 B 2560 B 2420 B
ML-DSA-65 NIST L3 1952 B 4032 B 3293 B
ML-DSA-87 NIST L5 2592 B 4896 B 4595 B
```
Elige **ML-DSA-65** para builds nuevos. L3 es la base recomendada.
### Llamada mínima posible (Python)
```python
from pqcrypto.sign import ml_dsa_65
pub, priv = ml_dsa_65.generate_keypair()
msg = b"el mensaje a firmar"
sig = ml_dsa_65.sign(priv, msg) # 3293 bytes
ok = ml_dsa_65.verify(pub, msg, sig) # lanza excepción si falla
```
### Llamada mínima posible (TypeScript)
```ts
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa';
const kp = ml_dsa65.keygen();
const msg = new TextEncoder().encode('el mensaje a firmar');
const sig = ml_dsa65.sign(msg, kp.secretKey);
const ok = ml_dsa65.verify(sig, msg, kp.publicKey); // boolean
```
## 3. SLH-DSA — FIPS 205
**Qué hace**: firma recorriendo un árbol Merkle profundo de firmas
de-un-solo-uso. Seguridad solo de la función hash. La primitiva de firma
más conservadora de la suite.
**Reducción de seguridad**: resistencia a colisiones y preimagen de SHA-2
o SHAKE. Sin asunción teórica de números, sin asunción de retículos.
**Parámetros** (cada uno viene en `s` para "firma pequeña" y `f` para
"firma rápida"):
```
parámetro cat. sec clave pub firma verify
──────────────────────────────────────────────────────────────
SLH-DSA-128s L1 32 B 7856 B rápido
SLH-DSA-128f L1 32 B 17088 B rápido
SLH-DSA-192s L3 48 B 16224 B rápido
SLH-DSA-192f L3 48 B 35664 B rápido
SLH-DSA-256s L5 64 B 29792 B rápido
SLH-DSA-256f L5 64 B 49856 B rápido
```
Variantes `s`: firma más pequeña, firma más lenta (minutos por clave).
Variantes `f`: firma más rápida (segundos), firma mayor.
Elige **SLH-DSA-128s** para raíces de firma de código. La verificación es
solo-hash y rápida en cualquier plataforma — incluyendo PHP puro sin
extensiones.
### Llamada mínima posible (Python)
```python
from pqcrypto.sign import sphincs_sha2_128s_simple as slh
pub, priv = slh.generate_keypair()
msg = b"hash de imagen de firmware"
sig = slh.sign(priv, msg)
ok = slh.verify(pub, msg, sig)
```
## 4. FN-DSA (Falcon) — FIPS 206 (borrador)
**Qué hace**: firma de retículos con las firmas más pequeñas de la suite.
**Estado**: borrador a mediados de 2026. Final esperado para 2026/2027.
**Por qué esperar**: Falcon depende de aritmética **de punto flotante** en
la implementación de referencia. Los riesgos de canal lateral y de portado
son reales. NIST aconseja contra despliegue hasta que FIPS 206 se finalice
y existan implementaciones validadas de tiempo constante.
**Cuándo elegirlo**: cargas con limitación de ancho de banda donde el
tamaño de firma importa más que la simplicidad de implementación
(actualizaciones de firmware IoT, algunos flujos internos de HSM).
## 5. El árbol de decisión
```
¿Necesitas encapsular una clave (TLS, JWE, MLS, envelope KMS)?
└─ ML-KEM-768
¿Necesitas firmar un token por mensaje (JWT, OCSP, firma de requests)?
├─ ¿Servicio de larga duración? → ML-DSA-65 híbrido con Ed25519
└─ ¿Perf por petición? → ML-DSA-65 solo (ya es rápido)
¿Necesitas firmar un artefacto de release (one-shot, verificado años)?
└─ SLH-DSA-128s (la elección "bóveda de 100 años")
¿Firma con restricción de ancho de banda?
└─ Espera a FIPS 206 (Falcon)
¿Verificación PHP pura en hosting compartido?
└─ SLH-DSA-128s — la verificación es solo-hash
```
## 6. Híbrido sigue siendo la jugada correcta
Guía de NIST en sí: en esta ventana de transición, **combina** un
algoritmo clásico con uno PQ para que una ruptura en cualquiera de los dos
te deje seguro. El patrón estándar:
```python
# Secreto compartido TLS híbrido = HKDF(X25519_ss || ML_KEM_ss, salt, info)
import os
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from pqcrypto.kem import ml_kem_768
# Corre ambos KEMs en paralelo.
pq_pub, pq_priv = ml_kem_768.generate_keypair()
pq_ct, pq_ss = ml_kem_768.encrypt(pq_pub)
x_ss = os.urandom(32) # en realidad: secreto compartido X25519
shared = HKDF(
algorithm=hashes.SHA384(), length=32,
salt=b'qbit404-hybrid', info=b'tls-1.3-key-derivation'
).derive(x_ss + pq_ss)
```
Esta es la misma construcción ratificada en `draft-ietf-tls-hybrid-design`.
Una ruptura de ML-KEM (improbable pero no-cero dada la juventud del campo)
no filtra el secreto compartido porque el atacante todavía tiene que
romper X25519 para la *misma sesión* — lo que requiere Shor, justo lo
que la migración PQ está evitando.
## 7. Checklist de despliegue
```
[ ] CBOM (cryptography BOM) — enumera cada clave.
[ ] TLS de borde: KEM híbrido anunciado.
[ ] Firma de código: rotación de raíz SLH-DSA planificada.
[ ] Emisores JWT: ML-DSA híbrido emitiendo.
[ ] SDK pinning: alias ML-KEM / ML-DSA, NO alias Kyber / Dilithium.
[ ] Validación CAVP: requerida para cargas FedRAMP / seguridad nacional.
```
Matriz de librerías (mediados de 2026):
| Stack | KEM | Firma | Firma hash | ¿Alias FIPS? |
|-------|-----|-------|------------|--------------|
| OpenSSL ≥ 3.4 | ML-KEM | ML-DSA | SLH-DSA | sí |
| BoringSSL | ML-KEM (X25519MLKEM768) | (planificado) | (planificado) | sí |
| Noble (JS/TS) | ml_kem768 | ml_dsa65 | slh_dsa_sha2_128s | sí |
| pqcrypto (Python) | ml_kem_768 | ml_dsa_65 | sphincs_sha2_128s | parcial |
| liboqs (C / FFI) | matriz completa | matriz completa | matriz completa | sí |
## 8. Referencias
- NIST FIPS 203 (ML-KEM), FIPS 204 (ML-DSA), FIPS 205 (SLH-DSA), FIPS 206
(FN-DSA, borrador)
- NIST SP 800-227 — implementando ML-KEM en TLS 1.3 (2025)
- Playbook de migración PQC conjunto BSI / ANSSI (2024)
- *Hybrid Key Exchange in TLS 1.3*, draft-ietf-tls-hybrid-design
> La suite PQC ya no "viene". Está publicada, congelada en parámetros, y
> soportada en OpenSSL y Noble. El trabajo restante es operacional:
> inventario, rotación, validación. Las matemáticas están hechas; el
> trabajo es tuyo.
---
### Q-day: cómo los analistas fijan la ventana entre 2029 y 2034
- URL: https://qbit404.com/es/news/q-day-countdown
- Date: 2026-06-02
- Author: Idris Vega
- Tags: q-day, mosca, prediccion, inteligencia, roadmap
## TL;DR
Seis estimaciones públicas de Q-day publicadas entre 2022-2026 aterrizan
todas entre **2029 y 2034** para la primera demostración creíble de Shor
contra RSA-2048. El desacuerdo es sobre qué año, no qué década. El
**teorema de Mosca** (X + Y > Z) traduce esa ventana a un plazo de
migración concreto que depende de la vida útil de tus secretos y de la
velocidad de tu migración. La mayoría de organizaciones ya llegan tarde.
## 1. Los pronósticos, alineados
```
año-predicción fuente estimación Q-day
─────────────────────────────────────────────────────────────────────────────
2022 Global Risk Institute (encuesta Mosca) 50% para 2031
2022 NSA CNSA 2.0 (mandato) objetivo "para 2033"
2023 IBM Quantum Network roadmap ~10k lógicos para 2033
2024 Google Quantum AI ~5k lógicos para 2030
2024 ENISA PQC migration report ventana 2029-2034
2025 Sevilla y Riedel criptoanalítico para 2030
2026 Actualización NIST IR 8547 "amenaza activa" para 2029
```
Las estimaciones son consistentes dentro de ±2 años. Más allá del número
titular, cada pronóstico especifica qué clase de demostración cuenta como
"Q-day":
- **Demo de laboratorio** factorizando un número no trivial: probablemente 2027-2028
- **Primera ruptura RSA-2048 creíble** (reclamada por un equipo serio): 2029-2032
- **Ruptura rutinaria de RSA-2048** (replicable, múltiples actores): 2033-2036
La ventana entre *primera* y *rutinaria* es de unos 4 años. Esa brecha
importa: durante ella, no sabes quién tiene la capacidad, así que debes
asumir que cualquier adversario a nivel estatal sí.
## 2. Teorema de Mosca
La formulación estándar, de Michele Mosca (uWaterloo / IQC):
> Si **X + Y > Z**, tienes un problema.
> - **X** = ¿cuánto tiempo debe permanecer confidencial tu secreto?
> - **Y** = ¿cuánto tardará tu migración a PQ?
> - **Z** = ¿cuánto falta hasta que un adversario cuántico pueda romper
> tu cripto actual?
Si tu secreto necesita estar seguro 10 años (historiales médicos,
propiedad intelectual, contratos) y tu migración tardará 3 años, no puedes
esperar más de `Z - 13` años antes de empezar. Con Z = 2030, eso significa
**2017**. Ya llegas 9 años tarde, y solo estás recuperando.
## 3. Una calculadora
```python
# mosca.py — traduce vida útil y tiempo de migración a un plazo.
from dataclasses import dataclass
from datetime import date
@dataclass
class MoscaInput:
secret_shelf_years: int # X — cuántos años deben permanecer confidenciales
migration_years: float # Y — tu tiempo de migración realista
qday_year: int # Z — tu Q-day asumido (elige conservador)
def deadline(m: MoscaInput) -> dict:
must_start_year = m.qday_year - m.secret_shelf_years - m.migration_years
today = date.today().year
slack = must_start_year - today
safe = m.secret_shelf_years + m.migration_years <= (m.qday_year - today)
return {
'must_start_by': int(must_start_year),
'years_of_slack': round(slack, 1),
'safe_today': safe,
'verdict': 'OK' if safe else 'TARDE',
}
# Ejemplos
print(deadline(MoscaInput(secret_shelf_years=10, migration_years=3, qday_year=2030)))
# → {'must_start_by': 2017, 'years_of_slack': -9, 'safe_today': False, 'verdict': 'TARDE'}
print(deadline(MoscaInput(secret_shelf_years=2, migration_years=1, qday_year=2032)))
# → {'must_start_by': 2029, 'years_of_slack': +3, 'safe_today': True, 'verdict': 'OK'}
print(deadline(MoscaInput(secret_shelf_years=25, migration_years=5, qday_year=2031)))
# → vida útil militar / archivo nacional — debió haber empezado en 2001.
```
Córrelo sobre tres de tus cargas reales. Las filas con "verdict: TARDE"
te dicen dónde gastar dinero primero.
## 4. Qué significa "tiempo de migración" realmente
Y rara vez es solo "instala OpenSSL 3.4". Para una organización no trivial
incluye:
1. **Inventario** (CBOM): cada clave en KMS, cada certificado, cada
emisor JWT, cada raíz de firma de código, cada clave de backup.
Típicamente **3-6 meses** para una empresa mediana.
2. **Piloto**: TLS híbrido en un servicio interno, JWT híbrido para un
flujo de auth. **3 meses**.
3. **Despliegue en borde**: load balancers, CDN, ingress público. **6-12
meses** coordinando con vendors.
4. **Servicios internos**: lo más duro porque cada equipo tiene que probar.
**12-24 meses**.
5. **Rotación de raíces** (firma de código, CA, claves raíz): one-shot,
pero bloqueado en tener verificadores PQ en todas partes. **6 meses**
de retraso tras cada otro paso.
Súmalo: 30-50 meses para una org real con exposición regulatoria. Eso es
Y ≈ 3-4 años. Con Q-day ≈ 2030 y 10 años de vida útil, debiste empezar en
2016.
## 5. Qué desencadena "Q-day" realmente
Q-day no es un evento único. Es una secuencia:
- **D0**: Primera demostración creíble. Las acciones de empresas RSA caen.
Reguladores cripto (NIST, BSI, ANSSI) emiten avisos de emergencia.
- **D0 + 6 meses**: Los ecosistemas de navegador fuerzan TLS híbrido,
cortan la emisión de certificados RSA-2048. Las CAs rotan raíces.
- **D0 + 1 año**: Las raíces de firma de código deben ser PQ. Distros
Linux, Microsoft, Apple, todos se comprometen a firma hash-based o
híbrida.
- **D0 + 2 años**: Soft-fork del protocolo Bitcoin a direcciones PQ si no
estaba ya en marcha.
- **D0 + 5 años**: RSA-2048 está "roto" en la mente pública. Cualquier
documento cifrado con cripto clásica y en manos de un adversario es
plaintext.
Puedes comprarte una opción en cada paso — actuando antes de D0.
## 6. Una línea de tiempo práctica
Si estás leyendo esto en 2026 y no has empezado:
```
Q2 2026 → Contrata / asigna un lead de migración cripto.
Q3 2026 → Inventario CBOM completo.
Q4 2026 → Barrido AES-128 → AES-256 completo. Auditoría de hash hecha.
Q1 2027 → TLS híbrido en el perímetro. Piloto interno.
Q3 2027 → JWT híbrido para auth entre servicios.
Q1 2028 → Raíces de firma de código rotadas a SLH-DSA.
Q3 2028 → Flujos de cara al cliente aceptan certificados híbridos.
Q1 2029 → Endpoints RSA-2048 legacy desactivados internamente.
Q3 2029 → Endpoints RSA-2048 legacy de cara al cliente desactivados.
```
Es una migración de 3,5 años, en paralelo. Es realista para una
organización de ~500 ingenieros. Si tienes menos, simplifica pero no
ralentices.
## 7. Referencias
- Mosca, *Cybersecurity in an era with quantum computers: will we be
ready?* (IEEE S&P, 2018) — origen de la formulación X+Y>Z
- Global Risk Institute, *Quantum Threat Timeline Report* (anual, 2019-)
- ENISA, *Post-Quantum Cryptography: Current State and Quantum Mitigation*
(2024)
- NIST IR 8547, *Transition to post-quantum cryptographic standards* (2024)
- NSA CNSA 2.0, *Announcing the Commercial National Security Algorithm
Suite 2.0* (2022)
> Q-day no es un plazo. Es el punto pasado el cual tus plazos anteriores
> dejan de importar. Los plazos que importan son los tuyos — y la mayoría
> ya pasaron.
---
### Cae RSA-2048: el estimado de 20M de qubits ruidosos, redibujado
- URL: https://qbit404.com/es/news/rsa-2048-falls
- Date: 2026-06-02
- Author: Mara Chen
- Tags: rsa, shor, estimado-recursos, cuantica, gidney
## TL;DR
Un módulo RSA de 2048 bits cae ante el algoritmo de Shor con **20 millones
de qubits físicos ruidosos** corriendo durante **unas 8 horas** bajo los
parámetros del estimado de Gidney y Ekerå (2019). Ese número se ha estrechado
entre 2024 y 2026 con mejores disposiciones de cirugía de retículos y
presupuestos de error físico más bajos — los estimados públicos actuales lo
sitúan en **~9-14 millones** para el mismo objetivo temporal. La conclusión
no se mueve: **RSA-2048 tiene fecha de caducidad**, y cualquier texto cifrado
que envíes hoy es grabable ahora y descifrable en los 2030s.
## 1. De dónde salió el 20M
El estimado seminal es Gidney y Ekerå, *How to factor 2048 bit RSA integers
in 8 hours using 20 million noisy qubits* (2019). El número titular asume:
- Tasa de error de puerta física **p = 10⁻³** (lo mejor del superconductor hoy).
- Código de superficie con **distancia d = 27**.
- Una fábrica de estados mágicos produciendo **>= 1 por μs**.
- Circuito de exponenciación modular optimizado en Toffolis.
El número es función cuadrática de la tasa de error físico. Reducir `p` a la
mitad reduce el conteo de qubits en un orden de magnitud.
## 2. Lo que se ha movido en 2024-2026
```
Gidney y Ekerå 2019 Webber et al. 2022 Sevilla y Riedel 2024
qubits físicos ~20.000.000 ~13.500.000 ~9.200.000
reloj ~8 horas ~8 horas ~5,6 horas
error de puerta 1e-3 5e-4 3e-4
distancia código 27 25 23
```
Lo que lo impulsa: mejoras en el ruteo de cirugía de retículos, circuitos de
multiplicación modular más pequeños, y la asunción de una tasa de error
físico futura de **3 × 10⁻⁴** — que los roadmaps de átomos neutros de Google
y superconductor de IBM proyectan para finales de los 2020s.
## 3. La ruptura, esbozada
La reducción funciona así:
```
RSA-2048 → factorización de enteros N
→ encontrar periodo r de f(x) = aˣ mod N ← paso cuántico de Shor
→ derivar p, q de gcd(a^(r/2) ± 1, N)
```
El paso 2 es el difícil cuánticamente. Clásicamente, encontrar el periodo es
exponencial. Shor lo extrae mediante la QFT en `O((log N)³)` operaciones.
Sobre `N` de 2048 bits:
- Qubits lógicos requeridos: `2n + 3 = 4099`.
- Conteo de Toffolis para exponenciación modular: `~9,4 × 10⁹`.
- T-states consumidos: `~3 × 10¹⁰`.
La traducción física depende de la distancia de código, throughput de la
fábrica de estados mágicos, y la sobrecarga de ruteo. La cifra de 9,2M asume
parámetros agresivos pero publicados.
## 4. Una factorización juguete sobre `N` pequeño
No puedes correr Shor sobre RSA-2048 todavía. Sí puedes correr la estructura
sobre `N` pequeño clásicamente, con la QFT reemplazada por una búsqueda de
periodo por fuerza bruta — suficiente para ver que el álgebra es la misma.
```python
# shor_demo.py — búsqueda de periodo clásica para la estructura de Shor
from math import gcd
from random import randrange
def find_period(a: int, N: int) -> int | None:
"""Búsqueda por fuerza bruta de r tal que a^r ≡ 1 (mod N). La QFT lo hace en tiempo poli."""
x = a % N
for r in range(1, N):
if x == 1:
return r
x = (x * a) % N
return None
def shor_factor(N: int, attempts: int = 20) -> tuple[int, int] | None:
if N % 2 == 0:
return (2, N // 2)
for _ in range(attempts):
a = randrange(2, N)
g = gcd(a, N)
if g > 1:
return (g, N // g)
r = find_period(a, N)
if r is None or r % 2 != 0:
continue
x = pow(a, r // 2, N)
if x == N - 1:
continue
p = gcd(x - 1, N)
q = gcd(x + 1, N)
if 1 < p < N:
return (p, N // p)
if 1 < q < N:
return (q, N // q)
return None
if __name__ == "__main__":
# Dos primos pequeños — lo que una máquina cuántica encontraría para N de 2048 bits real.
print(shor_factor(15)) # → (3, 5)
print(shor_factor(21)) # → (3, 7)
print(shor_factor(187)) # → (11, 17)
```
Esto corre en milisegundos para `N < 10⁶`. Para `N = 2^2048`, el bucle
clásico tarda más que la edad del universo. El bucle cuántico termina en 8
horas en una máquina tolerante a fallos — ese es todo el punto del algoritmo.
## 5. Un estimador de recursos
```python
# rsa_shor_estimate.py — coste aproximado de Shor para módulo RSA de n bits
def estimate(n_bits: int, p_phys: float = 3e-4) -> dict:
logical_qubits = 2 * n_bits + 3
toffoli = int(2.1 * (n_bits ** 3))
# Sobrecarga del código de superficie con tasa de error p_phys
d = max(7, round(2 + 2 * (-math.log10(p_phys))))
physical_per_logical = 2 * d * d
physical = logical_qubits * physical_per_logical
# Asumimos ~1 μs por Toffoli lógico con d=23-27
seconds = toffoli * 1e-6 / 100 # 100 fábricas de estados mágicos en paralelo
return {
'rsa_bits': n_bits,
'logical_qubits': logical_qubits,
'physical_qubits_orderof': physical,
'toffoli_count': toffoli,
'wallclock_hours_orderof': round(seconds / 3600, 1),
'code_distance': d,
}
import math
for n in [1024, 2048, 3072, 4096]:
print(estimate(n))
```
Salida para RSA-2048: ~4100 lógicos, ~5-10M físicos, ~8 horas de reloj.
Para RSA-4096: ~8200 lógicos, ~10-20M físicos, ~64 horas de reloj.
## 6. Qué significa esto en 2026
- **RSA-2048**: 5-10 años para romperlo bajo las trayectorias actuales. Ya
dentro de la ventana "harvest now, decrypt later".
- **RSA-3072**: 10-15 años. Mejora marginal — te compra una década, no
seguridad.
- **RSA-4096**: 15-20 años. Mismo algoritmo, misma ruptura, solo más grande.
El único escape de la curva de recursos es salir de la curva: firmas
post-cuánticas (ML-DSA, SLH-DSA) y KEMs (ML-KEM) no están en ella.
> Playbook de migración: ver `/es/news/post-quantum-crypto` para los patrones
> de TLS híbrido / JWT / firma de releases.
## 7. Referencias
- Gidney y Ekerå, *How to factor 2048-bit RSA integers in 8 hours using 20
million noisy qubits* (arXiv:1905.09749, 2019)
- Webber, Elfving, Weidt y Hensinger, *The impact of hardware specifications
on reaching quantum advantage in the fault tolerant regime* (AVS Quantum
Science, 2022)
- NIST IR 8547, *Transition to post-quantum cryptographic standards* (2024)
- Aviso NSA CNSA 2.0 (2022, actualizado 2024)
> Una clave RSA de 2048 bits enviada en 2026 puede seguir siendo válida en
> tu cadena de CA en 2032. La máquina cuántica no necesita existir hoy. Solo
> necesita existir antes de que esa clave caduque.
---
### Algoritmo de Shor vs Bitcoin: anatomía de una kill chain sobre la derivación de claves
- URL: https://qbit404.com/es/news/shor-bitcoin
- Date: 2026-05-22
- Author: Riku Saari
- Tags: shor, bitcoin, ecdsa, secp256k1, criptoanalisis
## TL;DR
El algoritmo de Shor reduce el **problema del logaritmo discreto sobre curvas elípticas (ECDLP)** a la **búsqueda de periodos**, que un ordenador cuántico tolerante a fallos resuelve en tiempo polinómico. Las firmas de Bitcoin viven sobre secp256k1, una curva ECDLP-dura. En cuanto exista un cuántico lógico suficientemente grande, **cualquier dirección que haya expuesto su clave pública en la cadena es recuperable**. Aproximadamente el 25% del BTC en circulación está hoy exactamente en ese estado — sobre todo salidas P2PK, direcciones P2PKH reutilizadas, y cualquier UTXO cuya transacción de gasto ya se haya transmitido pero no esté minada.
Este no es un problema de 2026. Es un **problema de ~2030 con una ventana de migración en 2026**.
## 1. La matemática, comprimida
ECDSA sobre secp256k1 elige un punto base `G` de orden primo `n ≈ 2^256` y un escalar privado `d ∈ [1, n-1]`. La clave pública es `P = d·G`. Firmar implica el nonce por mensaje `k`, produciendo `(r, s)` donde:
```
r = (k·G).x mod n
s = k^-1 · (H(m) + r·d) mod n
```
Un atacante clásico tendría que invertir la multiplicación escalar `d·G → d`, una instancia de ECDLP. El mejor ataque clásico conocido es rho de Pollard con ≈ √n ≈ 2^128 operaciones. Cómodamente irrompible.
Shor reformula ECDLP como un **problema del subgrupo oculto** sobre `Z_n × Z_n`. Dado un oráculo para la función:
```
f(a, b) = a·P + b·G
```
`f` tiene periodo `(d, -1) mod n`. La transformada cuántica de Fourier extrae ese periodo en `O((log n)^3)`. En concreto, una máquina tolerante a fallos rompe secp256k1 con aproximadamente:
- **2330 qubits lógicos** (Roetteler et al., 2017)
- **~10^9 puertas Toffoli**
- ~ **8 horas** de reloj a tasas de reloj lógicas proyectadas de ~10^4 Hz
Traducir lógico a físico con la sobrecarga actual del código de superficie (10^3–10^4 físicos por lógico) sitúa el coste en **2 × 10^7 – 2 × 10^8 qubits físicos**. Condor de IBM (1121 qubits, 2023) y los 1180 qubits de átomo neutro de Atom Computing (2024) siguen siendo **NISQ ruidosos** — muy por debajo del umbral, pero la brecha se cierra en una curva tipo Moore.
## 2. El inventario expuesto de BTC
Una dirección de Bitcoin solo revela su clave pública cuando se gasta un UTXO en esa dirección. El modelo de exposición tiene capas:
| Capa | Estado clave pública | BTC aprox (Q1 2026) |
|------|----------------------|----------------------|
| Salidas P2PK (pre-2010, era Satoshi) | Siempre expuesta | ~1.7M BTC |
| Direcciones P2PKH/P2WPKH reutilizadas | Expuesta tras primer gasto | ~2.5M BTC |
| P2PKH/P2WPKH/P2TR frescas sin gastar | Solo hash | ~14M BTC |
| Tx en mempool (transmitida, sin minar) | Expuesta ~10 min | ~50k BTC rotando |
Las dos primeras filas — **~4.2M BTC, aproximadamente 280 mil millones USD a precio actual** — son recuperables por cualquier actor que logre relevancia criptoanalítica primero. Las monedas de Satoshi están en este cubo.
La exposición del mempool es la **superficie de ataque activa**: un adversario con un cuántico suficientemente rápido puede romper la clave privada desde la firma transmitida antes de que se confirme la transacción, y luego correr una transacción con mayor comisión que redirige los fondos.
## 3. La kill chain, en concreto
```
[tx transmitida]
| la firma revela P = d·G
v
[harvest ahora] -> almacena tuplas (P, tx_hash)
v
[descifra después] -> Shor sobre P -> d
v
[forja gasto] -> firma nueva tx con d, RBF mayor comisión
```
El modelo **"harvest now, decrypt later"** no es teórico para Bitcoin. Adversarios estatales archivan demostrablemente toda la cadena más el mempool. Cada firma ya publicada es un pagaré contra capacidad cuántica futura.
Un adversario con capacidad de minero más Shor ni siquiera necesita correr — puede mantener un UTXO como rehén seleccionando silenciosamente en contra de cualquier tx que gaste desde una dirección vulnerable y sustituirla por la suya.
## 4. Cómo es la migración a nivel de protocolo
No hay ruta soft-fork que proteja claves ya expuestas. La migración tiene tres formas viables:
### 4a. Nuevo tipo de dirección con firmas PQ
Añadir un opcode (p. ej. `OP_CHECKDILITHIUM`) y una nueva versión de witness. Las firmas ML-DSA-44 son de 2420 bytes — ~75× más grandes que los 65 bytes de ECDSA. Los bloques engordan; los descuentos de witness al estilo SegWit lo mitigan. La parte dura es la **disciplina de rotación de carteras**: los usuarios deben mover cada moneda desde direcciones expuestas a direcciones PQ antes del Q-day.
### 4b. Firmas híbridas durante la transición
Requerir tanto una firma ECDSA como una Dilithium en el witness. Cinturón y tirantes. Duplica la sobrecarga de firma pero ofrece rollback limpio si la primitiva PQ resulta rota (ha ocurrido: SIKE en 2022, Rainbow en 2022, ambos finalistas NIST).
### 4c. Firmas hash de un solo uso (XMSS / SPHINCS+)
SPHINCS+ es sin estado y conservador — la seguridad se reduce solo a la función hash. Las firmas son grandes (~8 KB en NIST L1) pero la historia criptográfica es la más simple de verificar. Cobertura razonable para un protocolo monetario de capa base donde domina la cautela.
Se ha propuesto un soft-fork coordinado de **congelación** de direcciones legacy tras un flag-day, y es políticamente incendiario. Resultado probable: **direcciones PQ opt-in en 2027-2028**, monedas vulnerables en carteras activas migran, **monedas latentes terminan siendo drenadas** por quien cruce el umbral cuántico primero.
## 5. Una prueba juguete: la estructura de Shor sobre una curva pequeña
No puedes ejecutar un Shor de 256 bits en un portátil. Sí puedes ejecutarlo sobre una curva de 5 bits — instructivo, y la estructura es idéntica.
```python
# shor-ecdsa-sim.py (extracto) — fichero completo en Descargas
# Sustituto clásico de la búsqueda de periodos. Modela el oráculo cuántico f(a,b) = a*P + b*G
# sobre la curva juguete E: y^2 = x^3 + 2x + 3 (mod 97). Orden n = 5.
def shor_ecdlp_demo(P, G, n):
# En una ejecución real, esta extracción del periodo la hace la QFT;
# aquí brutaleamos la estructura para exponerla.
for a in range(1, n):
for b in range(1, n):
if scalar_mult(a, P) == scalar_mult(b, G):
# a*d ≡ b (mod n), así que d = b * a^-1 mod n
return (b * pow(a, -1, n)) % n
return None
```
El fichero completo en el bundle de descargas incluye:
- Ley de grupo de secp256k1 para curiosos
- Una demo de 5 bits de extremo a extremo
- Un estimador que toma tu `n` y te dice el número de qubits lógicos que necesitaría Shor
## 6. Qué hacer, este trimestre
1. **Audita tu exposición**: para cada cartera que controles, lista direcciones que hayan sido alguna vez *emisoras* de una transacción. Esas claves son públicas. Barre los fondos a una dirección fresca nunca-gastada-desde. P2TR (Taproot) es solo-hash hasta el primer gasto — úsalo.
2. **Deja de reutilizar direcciones**. Es una norma de higiene que preexiste a Shor y ahora tiene dientes.
3. **Evita canales Lightning con grandes saldos latentes en nodos legacy**. Las transacciones de apertura de canal revelan claves públicas.
4. **Vigila BIP-360 y el BIP-?? (firmas PQ)** para propuestas a nivel de protocolo. La disciplina de migración va a ser un problema de UX, no de matemática.
5. **Si operas un exchange**, comienza ya la rotación de cold-storage. La responsabilidad custodia escala linealmente con la proximidad cuántica.
## 7. Lectura recomendada
- Roetteler, Naehrig, Svore, Lauter — *Quantum Resource Estimates for Computing Elliptic Curve Discrete Logarithms* (2017)
- Aggarwal, Brennen, Lee, Santha, Tomamichel — *Quantum Attacks on Bitcoin, and How to Protect Against Them* (2018)
- Stewart, Ilie, Zamyatin et al. — *Committing to Quantum Resistance* (2018)
- NIST IR 8413 — *Status Report on the Third Round of the NIST Post-Quantum Cryptography Standardization Process* (2022)
> El reloj arranca no cuando llega el Q-day, sino cuando el primer adversario cree que el Q-day está más cerca que tu plazo de migración. Ese momento puede haber pasado ya.
---
### El playbook del hacker cuántico: qué cae primero cuando llega el Q-day
- URL: https://qbit404.com/es/news/quantum-hacking
- Date: 2026-05-15
- Author: Mara Chen
- Tags: cuantica, redteam, ttp, grover, shor, infraestructura
## TL;DR
Cuando llegue el Q-day — definido aquí como la primera ruptura Shor pública sobre una clave RSA-2048 — el ataque no se parece a una película. Se parece a **sesiones TLS de 2019 descifrándose en silencio**, **actualizaciones de software firmadas falsificadas retroactivamente**, y **claves de larga duración de túneles VPN volviéndose públicas**. El trabajo real de hackeo ocurre **hoy**, en forma de cosecha, indexado y selección de objetivos. La máquina cuántica es un oráculo de factorización de un solo disparo enchufado a un pipeline que ya existe.
Este post es un mapa de triaje: qué cae, en qué orden, con qué esfuerzo.
## 1. Los dos ejes criptográficos
Solo dos algoritmos cuánticos importan en la práctica para la cripto actual:
- **Shor** rompe primitivas asimétricas cuya seguridad se reduce a factorización entera o logaritmo discreto. Eso mata **RSA, DSA, ECDSA, ECDH, DH, ElGamal**, por completo.
- **Grover** divide a la mitad la longitud efectiva de clave de cifrados simétricos y hashes. AES-128 pasa a ~64 bits de seguridad (roto). AES-256 pasa a 128 (sigue bien). SHA-256 pasa a ~128 bits de preimagen (bien por ahora). El arreglo es **duplicar la clave**.
Todo lo demás — criptoanálisis de retículos, ataques basados en códigos, mejoras estructuradas — es de grado investigación, no listo para pipeline. Planifica contra Shor y Grover; vigila el resto.
## 2. Inventario, por orden de dolor
Esta es la versión fusionada de NIST SP 800-208, el informe de migración PQC 2024 de ENISA, y una buena dosis de folklore de respuesta a incidentes.
### Capa 0 — cae al instante, armable en semanas
| Sistema | Mecanismo | Qué ocurre |
|---------|-----------|------------|
| TLS 1.2/1.3 con KEX clásico (ECDHE, RSA) | Shor sobre la clave efímera | Todas las sesiones capturadas se descifran. Incluye la mayoría del tráfico pre-2025 aún archivado. |
| Firma de código (Authenticode, notarización Apple, apt/dnf) | Shor sobre la clave de firma | Falsificación retroactiva: malware firmado que valida como Microsoft, Apple, tu distro. |
| Auth SSH por clave (RSA, ECDSA) | Shor sobre la clave pública | Compromiso de identidad de larga duración. Cada servidor donde hayas hecho SSH con esa clave queda expuesto. |
| Email PGP/SMIME | Shor sobre subclaves | El archivo de correo cifrado se descifra. El correo firmado histórico puede ser re-forjado. |
| ECDSA blockchain (BTC, ETH legacy) | Shor sobre pubkeys expuestas | Ver: shor-bitcoin |
### Capa 1 — cae al instante, armarlo es logísticamente más duro
| Sistema | Por qué es más lento |
|---------|----------------------|
| WPA2/WPA3 enterprise (EAP-TLS) | Requiere falsificar cadena de cert, pero factible. |
| VPN IPSec con IKEv2 + RSA/ECDSA | Claves de túnel expuestas retroactivamente. Necesita archivo PCAP. |
| DNSSEC | El envenenamiento de resolver queda firmado e indistinguible del autoritativo. |
| Smart cards / tokens hardware (PIV, YubiKey FIDO U2F) | RSA-2048 o P-256 dentro; no se necesita posesión física para spoofing. |
### Capa 2 — simétrico, solo debilitado
| Sistema | Bits efectivos post-Grover | Veredicto |
|---------|----------------------------|-----------|
| AES-128 | ~64 | Romper pronto. Migrar a AES-256. |
| AES-256 | ~128 | Seguro. |
| SHA-256 (preimagen) | ~128 | Seguro por ahora. |
| SHA-256 (colisión vía BHT) | ~85 | Arriesgado para compromisos de larga vida. SHA-384 / SHA3-384. |
| ChaCha20-Poly1305 | ~128 | Seguro. |
| HMAC | ~mitad-de-clave | Si la clave ≥ 256 bits, bien. |
### Capa 3 — cae solo si la candidata PQ es rota después
| Sistema | De qué depende |
|---------|----------------|
| ML-KEM (Kyber) | Dureza de Module-LWE. Los mejores ataques clásicos y cuánticos conocidos son exponenciales. |
| ML-DSA (Dilithium) | Module-LWE / Module-SIS. |
| SPHINCS+ | Solo función hash. La más conservadora. |
| FALCON | Retículo NTRU. Los riesgos de implementación (punto flotante) son el principal peligro. |
## 3. El pipeline real del atacante
Un atacante con capacidad Shor no apunta un cuántico a internet. El flujo se ve así:
```
[ARCHIVO] ---> 20 PB de PCAPs TLS indexados, certs de firma de código,
tx blockchain, handshakes VPN, claves PGP
|
[TRIAJE] ---> rankea objetivos por ROI:
- intercepciones de comms soberanas (top)
- cadena de suministro software
- archivo PGP ejecutivo
- UTXOs blockchain con pubkeys expuestas
- túneles VPN de larga duración
|
[FACTORIZA] ---> alimenta N (módulo RSA) o P (pubkey EC) a Shor
~horas por objetivo en máquina tolerante a fallos
|
[REPLAY] ---> usa la clave privada recuperada fuera de banda:
- descifra archivo
- firma actualización falsificada
- re-firma correo falsificado
- barre crypto
```
El cuello de botella no es factorizar. Es la **selección de objetivos**. Los adversarios con capacidad Shor serán **conservadores con los ciclos**: cada ejecución de factorización es una operación de varias horas y muchos megavatios. Operativamente, espera un puñado de descifrados "trofeo" públicamente atribuibles, mientras el grueso del impacto permanece bajo la línea como inteligencia clasificada.
## 4. Detectando "harvest now" hoy
No puedes detener el archivado. A veces puedes detectarlo. Patrones a buscar:
- **Colectores pasivos de larga duración** en rutas de infraestructura que no controlas — tomas en cables submarinos, puntos de peering de ISPs, desvíos BGP.
- **Capturas repetidas de handshake completo** sin reanudación de sesión — los colectores quieren el intercambio de claves, no el bulto de datos.
- **TCP RST inexplicable tras el handshake** en servicios internos desde IPs externas — captura selectiva.
- **Pico en queries DNSSEC a zonas de alto valor** sin resolución — pre-grabación.
Despliega `harvest-detector.py` (en las descargas) en nodos perimetrales para marcar sesiones TLS que parecen colección en vez de uso:
```python
# extracto — fichero completo en descargas
def looks_like_collection(flow):
# handshake completo, sin ticket de reanudación honrado, sin datos en bulto
if not flow.full_handshake: return False
if flow.session_resumed: return False
if flow.app_data_bytes > 4096: return False
if flow.duration_ms < 2000: return False
return True
```
Los falsos positivos no son triviales — health checks, escáneres, algunos CDNs. Afina los umbrales.
## 5. Defensas, ordenadas por ROI
1. **Deja de usar AES-128.** Trivial. Hazlo este sprint. AES-256 en todas partes.
2. **TLS híbrido**: el borrador NIST (`X25519MLKEM768`) viene en OpenSSL 3.4 y BoringSSL. Actívalo en cada endpoint de borde. Los navegadores (Chrome 124+, Firefox 132+) ya lo hablan.
3. **Cambia la firma de código a un esquema basado en hash** (SPHINCS+ o XMSS). Las firmas son de 8–40 KB pero firmar es raro; el coste es aceptable.
4. **Rota cada clave asimétrica de larga duración en un ciclo de 12 meses**. Limita el valor de las sesiones pasadas cosechadas, incluso clásicamente.
5. **Mueve las claves SSH de usuario a Ed25519 ahora y a ML-DSA cuando OpenSSH lo entregue (en curso)**. Evita RSA-2048 para cualquier clave nueva.
6. **Para PGP**, genera claves híbridas frescas (ECC + ML-KEM) cuando aterrice la rama borrador de GnuPG. Republica.
7. **Para las blockchains que tengas**, barre fondos a direcciones cuya pubkey nunca haya sido expuesta. Activa una alerta para el primer estándar de dirección PQ de tu cadena.
8. **Audita tus tokens hardware**. RSA-2048 dentro de YubiKey serie 5 está al final de vida. El firmware 5.7 (2024) soporta brainpoolP384; la próxima generación enviará PQ.
9. **Construye un inventario de claves.** No puedes rotar lo que no puedes enumerar. CycloneDX tiene una especificación CBOM (cryptography BOM) — adóptala.
10. **Prueba PQ en CI ya.** Liboqs ofrece reemplazos para OpenSSL. Los modos de fallo que encuentras en CI son modos de fallo que no encuentras en respuesta a incidentes.
## 6. Perspectiva de actores de amenaza
- **Nivel estatal**: construyendo o contratando máquinas tolerantes a fallos. NSA (CNSA 2.0) y el MSS chino exigen migración PQ para ~2030 en sistemas de seguridad nacional. No esperan al roadmap público.
- **Crimen organizado**: aún sin capacidad cuántica. Alquilará capacidad a servicios cuánticos en nube alineados con estados dentro de 2-3 años del primer Q-day público.
- **Amenazas internas**: completamente inalteradas. PQ no te protege de tu sysadmin.
- **Script kiddies**: irrelevantes durante al menos una década. La explotación cuántica no será descargable.
## 7. Lectura recomendada
- ENISA — *Post-Quantum Cryptography: Current State and Quantum Mitigation* (2024)
- NSA — *CNSA 2.0 Suite* (2022, actualizado 2024)
- Teorema de Mosca (informal): si **X + Y > Z**, tienes un problema, donde X es vida útil del secreto, Y es tiempo para migrar, Z es tiempo al Q-day.
- BSI — *Migration to Post-Quantum Cryptography* (2024)
> El atacante no necesita un cuántico para empezar el ataque. Solo necesita creer que uno viene. Trata tu tráfico TLS saliente actual como si fuera una postal. Ese es el modelo de amenaza.
---
### Criptografía post-cuántica en producción: Next.js, Python y PHP, el código que funciona
- URL: https://qbit404.com/es/news/post-quantum-crypto
- Date: 2026-05-08
- Author: Idris Vega
- Tags: pqc, kyber, dilithium, nextjs, python, php, implementacion
## TL;DR
NIST finalizó **ML-KEM (FIPS 203)** para encapsulado de clave, **ML-DSA (FIPS 204)** para firmas digitales, y **SLH-DSA (FIPS 205)** para firmas basadas en hash. **FALCON (FIPS 206)** sigue en borrador. Para 2026, la postura segura es **híbrida**: primitiva clásica XOReada con una primitiva PQ, de forma que romper el resultado requiera romper *ambas*. Este post es el código que funciona, en tres stacks, para las cargas más comunes: KEM tipo TLS, firma de documentos, emisión de tokens de API.
Si la librería que coges no dice **ML-KEM / ML-DSA**, está desactualizada. Los nombres cambiaron desde Kyber / Dilithium cuando aterrizaron los estándares.
## 1. La shortlist
| Carga | Usar | Evitar |
|-------|------|--------|
| Intercambio de claves / KEM | **ML-KEM-768** (NIST L3) híbrido con X25519 | Kyber-512 puro, RSA puro |
| Firmas digitales | **ML-DSA-65** (NIST L3) híbrido con Ed25519 | RSA-PSS, ECDSA aislados |
| Firmas basadas en hash (firmware, raíces) | **SLH-DSA-128s** (firma pequeña) o **SLH-DSA-128f** (firma rápida) | XMSS sin higiene de estado |
| Simétrico | AES-256-GCM, ChaCha20-Poly1305 | AES-128 |
| Hash | SHA-384, SHA3-384, BLAKE3 | SHA-256 puro para commits de larga vida |
ML-KEM es lo que cableas en tu TLS, en tu envelope encryption, en el establecimiento de sesión. ML-DSA es lo que cableas en tus JWTs, tu firma de código, tu auth de cliente. SLH-DSA es lo que cableas en cosas que firmas **una vez** y quieres verificar en **2080**.
## 2. Por qué híbrido
Las primitivas PQ son jóvenes. SIKE (2022) fue candidata NIST ronda 4. En un año se rompió en un portátil. Rainbow (2022) llegó a ronda 3, luego cayó. Los esquemas de retículo de la línea principal (Kyber/Dilithium/Falcon) han sobrevivido a escrutinio fuerte, pero el campo se mueve. **Híbrido paga 2× de sobrecarga en cable** y elimina el problema del punto único de fallo.
La construcción:
```
shared_secret = KDF( X25519_ss || ML-KEM_ss || transcript )
```
Ambas entradas están criptográficamente comprometidas. Romper una no le da nada al atacante sin romper también la otra.
## 3. Next.js — un endpoint KEM híbrido
Usa [`@noble/post-quantum`](https://github.com/paulmillr/noble-post-quantum) para ML-KEM y `@noble/curves` para X25519. Ambas son TypeScript auditado y sin dependencias. El kit en descargas es un Route Handler de Next.js que funciona.
```ts
// app/api/kem/route.ts
import { NextResponse } from 'next/server';
import { ml_kem768 } from '@noble/post-quantum/ml-kem';
import { x25519 } from '@noble/curves/ed25519';
import { hkdf } from '@noble/hashes/hkdf';
import { sha384 } from '@noble/hashes/sha2';
import { randomBytes } from '@noble/hashes/utils';
// Generación de keypair del servidor — hecha una vez, persistida en un KMS en producción.
function loadServerKeys() {
const kemSeed = process.env.PQC_KEM_SEED!; // 64 chars hex
const xSeed = process.env.PQC_X25519_SEED!;
const kemKp = ml_kem768.keygen(hexToBytes(kemSeed));
const xPriv = hexToBytes(xSeed);
const xPub = x25519.getPublicKey(xPriv);
return { kemKp, xPriv, xPub };
}
export async function GET() {
// Publica ambas claves públicas; el cliente encapsula contra ambas.
const { kemKp, xPub } = loadServerKeys();
return NextResponse.json({
suite: 'hybrid-x25519-mlkem768',
kem_pub: bytesToB64(kemKp.publicKey),
x_pub: bytesToB64(xPub),
});
}
export async function POST(req: Request) {
const body = await req.json();
const { kemKp, xPriv } = loadServerKeys();
const kemCt = b64ToBytes(body.kem_ct);
const xClient = b64ToBytes(body.x_client_pub);
const ssKem = ml_kem768.decapsulate(kemCt, kemKp.secretKey);
const ssX = x25519.getSharedSecret(xPriv, xClient);
const transcript = sha384(concat(b64ToBytes(body.kem_ct), xClient));
const sharedSecret = hkdf(sha384, concat(ssX, ssKem), transcript, 'qbit404-hybrid', 32);
// sharedSecret ya es tu clave de sesión AES-256-GCM
return NextResponse.json({ ok: true, sid: bytesToB64(sha384(sharedSecret).slice(0, 16)) });
}
```
Lado cliente, en el primer contacto:
```ts
// navegador
async function establish(serverPubs: { kem_pub: string; x_pub: string }) {
const { ml_kem768 } = await import('@noble/post-quantum/ml-kem');
const { x25519 } = await import('@noble/curves/ed25519');
const xClientPriv = crypto.getRandomValues(new Uint8Array(32));
const xClientPub = x25519.getPublicKey(xClientPriv);
const { cipherText, sharedSecret: ssKem } =
ml_kem768.encapsulate(b64ToBytes(serverPubs.kem_pub));
const ssX = x25519.getSharedSecret(xClientPriv, b64ToBytes(serverPubs.x_pub));
// ... deriva la clave de sesión idéntica al servidor
return { ssKem, ssX, kemCt: cipherText, xClientPub };
}
```
**Tamaño en cable**: el ciphertext ML-KEM-768 es de 1088 B, clave pública 1184 B. X25519 añade 32 B. El handshake total está dominado por el KEM. Cachea el bundle público del servidor agresivamente.
**Benchmark (M2 MBP, Node 20)**: keygen 0.6 ms, encaps 0.7 ms, decaps 0.7 ms. Más rápido que ECDH en la misma máquina.
## 4. Python — servicio de firma ML-DSA
Usa [`pqcrypto`](https://github.com/kpdemetriou/pqcrypto) (envuelve las impls de referencia de PQClean). Para producción, prefiere `liboqs-python` si necesitas artefactos validados NIST-CAVP.
```python
# pqc_sign.py — emisión de tokens de API firmados PQ
from pqcrypto.sign import ml_dsa_65
from nacl.signing import SigningKey # Ed25519
import json, time, hashlib, base64
def b64(b): return base64.urlsafe_b64encode(b).rstrip(b'=').decode()
def ub64(s): return base64.urlsafe_b64decode(s + '=' * (-len(s) % 4))
class HybridSigner:
def __init__(self, ed_seed: bytes, ml_dsa_pub: bytes, ml_dsa_priv: bytes):
self.ed = SigningKey(ed_seed)
self.pq_pub = ml_dsa_pub
self.pq_priv = ml_dsa_priv
@classmethod
def generate(cls, ed_seed: bytes):
pq_pub, pq_priv = ml_dsa_65.generate_keypair()
return cls(ed_seed, pq_pub, pq_priv)
def issue(self, claims: dict) -> str:
header = {'alg': 'EdDSA+ML-DSA-65', 'typ': 'JWT-HYBRID'}
payload = {**claims, 'iat': int(time.time())}
h_b = b64(json.dumps(header, separators=(',',':')).encode())
p_b = b64(json.dumps(payload, separators=(',',':')).encode())
msg = f"{h_b}.{p_b}".encode()
sig_ed = self.ed.sign(msg).signature
sig_pq = ml_dsa_65.sign(msg, self.pq_priv)
return f"{h_b}.{p_b}.{b64(sig_ed)}.{b64(sig_pq)}"
def verify(self, token: str) -> dict | None:
try:
h, p, s_ed, s_pq = token.split('.')
msg = f"{h}.{p}".encode()
self.ed.verify_key.verify(msg, ub64(s_ed))
ml_dsa_65.verify(msg, ub64(s_pq), self.pq_pub)
return json.loads(ub64(p))
except Exception:
return None
```
**Tamaño en cable**: la firma ML-DSA-65 es de **3293 bytes**. Con los 64 bytes de Ed25519 estás en ~4.4 KB de material de firma por token. Para casos JWT esto es grande — tu cabecera auth pasa a ~6 KB tras base64. Considera una cookie de sesión server-side que referencie un token PQ guardado en servidor en vez de empujar el token al navegador.
**Benchmark (M2 MBP, Python 3.12)**: firmar 1.4 ms, verificar 0.6 ms.
## 5. PHP — mensajes firmados retrocompatibles
PHP 8.x no tiene PQ nativo. Usa [`openssl_pkey_ed25519`](https://www.php.net/manual/en/openssl.constants.php) para la mitad clásica y `libsodium-php` con la extensión experimental ML-KEM, o invoca un binario verificado. El kit en descargas trae un binding FFI a `liboqs` (`PqcKem.php`, `PqcSign.php`, `SlhDsa128sVerify.php`) más un verificador CLI (`verify_release.php`) — misma forma en cable que las mitades Next.js y Python.
```php
b64, 'slh' => b64]
// pub: ['ed' => b64, 'slh' => b64]
$ed_ok = sodium_crypto_sign_verify_detached(
base64_decode($sig['ed']),
$message,
base64_decode($pub['ed'])
);
if (!$ed_ok) return false;
return SlhDsa128sVerify::verify(
$message,
base64_decode($sig['slh']),
base64_decode($pub['slh'])
);
}
// Uso de ejemplo en un receptor webhook
header('Content-Type: application/json');
$raw = file_get_contents('php://input');
$envelope = json_decode($raw, true);
$pub_bundle = json_decode(file_get_contents(__DIR__ . '/peer_pubs.json'), true);
if (!verify_hybrid($envelope['msg'], $envelope['sig'], $pub_bundle)) {
http_response_code(401);
echo json_encode(['err' => 'sig_invalid']);
exit;
}
echo json_encode(['ok' => true]);
```
**Por qué SLH-DSA en PHP**: la verificación es puro hash (SHA-256 por debajo), así que puedes enviar un verificador PHP sin dependencias en hosting compartido. Firmar es más pesado (querrás un proceso firmante en Python/Rust para eso); verificar en webhooks PHP va bien.
**Tamaño en cable**: la firma SLH-DSA-128s es de **7856 bytes**. Grande. Úsalo solo donde el modelo firmado-una-vez-verificado-muchas-veces domine: artefactos de release, SBOMs, procedencia de código.
## 6. Orden de migración, práctico
1. **Inventario** de cada endpoint TLS, cada clave en tu KMS, cada lugar donde vive un JWT. Cryptography BOM (CBOM). No puedes migrar lo que no puedes encontrar.
2. **Añade TLS híbrido en el borde** (Cloudflare, fastly, ALB) — casi siempre un flag de config en 2026. Ganancia gratis, sin cambios de cliente para navegadores que lo hablen.
3. **Rota raíces de firma de código a SLH-DSA**. Es la migración de mayor impacto porque los artefactos que firmas hoy se verificarán durante 10-20 años.
4. **JWTs híbridos para auth entre servicios**. Dentro de tu VPC controlas ambos extremos; el coste de tamaño es irrelevante.
5. **Tokens de cara al cliente al final**: mantén clásico hasta que las libs del navegador sean uniformes, luego añade una claim `pq=1` y acepta solo híbrido.
6. **Audita y retira tu AES-128**. Sed trivial en configs.
7. **Documenta una revisión de migración trimestral**. Métela en tu calendario de seguridad; los estándares PQ iterarán.
## 7. Trampas
- **Parámetros equivocados**: Kyber-512 es un set NIST L1, bien para handshakes efímeros, **no** bien para confidencialidad a largo plazo. Por defecto L3 (ML-KEM-768, ML-DSA-65).
- **Bigints no constant-time en implementaciones JS**: no te lo montes por tu cuenta. `@noble/post-quantum` es cuidadoso con constant-time; `pqcrypto` envuelve PQClean reference, que es best-effort.
- **Reuso de estado en esquemas con estado** (XMSS): catastrófico. SLH-DSA es sin estado precisamente para esquivar ese footgun.
- **Mezclar Kyber pre-estandarización con ML-KEM FIPS 203**: no son wire-compatible. Mira las notas de release de tu librería.
- **Implementaciones Falcon en punto flotante sobre hardware no IEEE-754**: bugs sutiles. Usa el binding FFI a una impl C revisada.
- **Generación de números aleatorios**: PQ amplifica el coste de un RNG malo. Solo fuentes del SO.
## 8. Lectura recomendada
- NIST FIPS 203, 204, 205 (2024)
- *PQC migration playbook*, BSI / ANSSI conjunto (2024)
- *Hybrid Key Exchange in TLS 1.3*, draft-ietf-tls-hybrid-design
- Bernstein, Buchmann, Dahmen — *Post-Quantum Cryptography* (libro, sigue siendo el mejor primer)
> "Híbrido es lo que eliges cuando no te has decidido. No nos hemos decidido, y no deberías fingir que sí." — Un evaluador de NIST, off the record, 2024.
Los kits en descargas son MIT-licensed y self-contained. Suéltalos en un repo, ejecuta el directorio `examples/` incluido, y tienes una superficie PQ funcionando en una tarde.
---
### Escáner de Vulns: manual del operador y catálogo de detección
- URL: https://qbit404.com/es/news/scanner-manual
- Date: 2026-05-01
- Author: Mara Chen
- Tags: scanner, manual, vulnerabilidades, opsec, owasp
## Qué es esta herramienta
`qbit404/scanner` es un escáner **no intrusivo** dirigido por fingerprints y firmas. No explota. Sondea con peticiones seguras y bien conocidas y empareja respuestas contra un catálogo interno de firmas conocidas-vulnerables (etiquetadas con CVE), malas configuraciones, y perfiles malos conocidos de cabeceras / cookies / TLS. Los matchers son puro patrón + aritmética de versión; nada de lo que el escáner envía es destructivo ni amplificador de tasa.
Está pensado para **sistemas que posees o tienes autorización escrita para probar**. Apuntarlo a terceros es delito en la mayoría de jurisdicciones.
## Qué cubre
El catálogo trae 7 módulos. Los módulos se activan/desactivan por escaneo.
### 1. Higiene TLS
- Validez de cadena de certificados, expiración < 30 días
- Auto-firmado en producción
- Algoritmo de firma débil en cert (MD5, SHA-1, RSA < 2048)
- Enumeración de cipher suites; marca: NULL, EXPORT, RC4, 3DES, modo CBC en TLS 1.0/1.1
- Versiones de protocolo: cualquier TLS 1.0 / 1.1 / SSL 3.0 / SSL 2.0 aceptado
- HSTS ausente / max-age muy bajo / sin `includeSubDomains`
- Ausencia de OCSP stapling
- Heartbleed (CVE-2014-0160) — envía un heartbeat benigno con longitud normal, comprueba longitud de respuesta
- ROBOT (CVE-2017-13099) — fingerprint, sin explotación
- **Preparación PQ**: sondea por anuncio KEX híbrido (X25519MLKEM768); marca ausencia en endpoints de cara a internet
### 2. Cabeceras HTTP y cookies
- Faltan: `Content-Security-Policy`, `X-Content-Type-Options`, `X-Frame-Options` / `frame-ancestors`, `Referrer-Policy`, `Permissions-Policy`
- CORS: `Access-Control-Allow-Origin: *` con credenciales
- Cookies sin `Secure`, `HttpOnly`, `SameSite`
- Disclosure de Server / X-Powered-By / X-AspNet-Version
- `Cache-Control` en endpoints autenticados
### 3. Fingerprints de frameworks / CMS conocidos
El escáner pasea por una lista de rutas trilladas (`/wp-login.php`, `/administrator/`, `/.git/config`, `/server-status`, `/actuator/env`, `/api/v1/swagger.json`, `/.env`, ...) y empareja firmas de cuerpo / cabecera contra el feed CVE:
- WordPress < 6.4.x — `/wp-json/wp/v2/users` expuesto (enumeración estilo CVE-2017-5487)
- Drupal < 7.58 — fingerprint Drupalgeddon2 (CVE-2018-7600)
- Joomla < 3.4.6 — fingerprint cadena RCE
- Apache HTTP < 2.4.49/2.4.50 — Path traversal (CVE-2021-41773 / 42013)
- nginx < 1.20.0 — heap overflow del resolver DNS (CVE-2021-23017)
- Tomcat < 9.0.71 — ghostcat (CVE-2020-1938)
- Spring Boot Actuator `/env`, `/heapdump`, `/jolokia` expuestos
- Jenkins script console, lectura anónima
- Confluence (CVE-2022-26134, CVE-2023-22515) fingerprints
- Atlassian Jira `/rest/api/2/user/picker?query=`
- GitLab enumeración de usuarios sin auth
- Log4Shell (CVE-2021-44228) — fingerprint JNDI en cabeceras HTTP vía callback canary (solo si proporcionas tu propio host canary)
### 4. Superficie API / OpenAPI
- Spec en `/openapi.json` / `/swagger.json` / `/v2/api-docs` — enumera endpoints, marca cualquiera que devuelva 200 a anónimo
- Fingerprints BOLA / IDOR: parámetros `id` en path cuyas respuestas cambian pero el alcance de auth no
- Páginas de error verbosas que filtran stack traces
- Introspección GraphQL activa en producción
### 5. Autenticación y sesión
- Formularios de login que postean por HTTP
- La respuesta de login filtra enumeración de usuarios (error distinto para "no existe ese usuario" vs "contraseña incorrecta")
- JWT `alg: none` aceptado
- JWT firmado con secreto HS256 débil (diccionario offline contra un token capturado)
- OAuth: redirect URIs abiertos, PKCE ausente en clientes públicos
- Fijación de sesión: el servidor acepta cookie de sesión pre-establecida
### 6. Cloud / metadata
- S3 buckets abiertos vía convenciones de nombrado conocidas
- Containers de Azure blob públicos
- Listado de buckets GCS sin auth
- Kubernetes `/api/v1/namespaces` anónimo
- Endpoint de metadata cloud alcanzable a través de sondas SSRF (solo con opt-in explícito del objetivo)
- Registry docker público `/v2/_catalog`
### 7. Auditoría de preparación cuántica
- Endpoint TLS anuncia o no KEX híbrido PQ
- El campo `alg` del JWT incluye solo EdDSA / RSA-PSS — marca para migración híbrida
- Algoritmo de firma del certificado — marca solo-clásicos para activos con validez > 5a
- Frescura de la cadena de cert de firma de código
- Fingerprint `Server:` revelado mapeado contra el tracker PQ-ready de qbit404
## Qué no hace
- No explota. El check de Log4Shell requiere que proporciones un host canary para que el escáner confirme un callback; nunca carga un payload por JNDI.
- No hace brute-force a formularios de login en vivo más allá del diccionario offline de JWT.
- No hace DoS, amplificación de tráfico ni tormentas de sesión.
- No pivota. Cada objetivo se escanea de forma aislada.
## Cómo usarlo
El frontend en `/tools/scanner` te guía por:
1. **Check de autorización**: confirma que tienes permiso para escanear el objetivo.
2. **Entrada de objetivo**: `https://example.com` o un rango IP que poseas.
3. **Selección de módulos**: TLS, cabeceras, CMS, API, auth, cloud, PQ — toggle.
4. **Intensidad**: ligera (10 req), normal (50 req), exhaustiva (250 req). Por defecto normal.
5. **Ejecutar**.
El resultado es un informe priorizado:
- **Crítico**: RCE / bypass de auth conocido con exploit público. Suelta todo.
- **Alto**: versión vulnerable + exploit público + config estándar. Parchea esta semana.
- **Medio**: misconfig con vector de ataque creíble. Planifica.
- **Bajo**: higiene. Mete en el próximo pase de hardening trimestral.
- **Info**: sin hallazgo, solo señal.
Cada hallazgo incluye: id CVE / CWE, regla origen, evidencia cruda (snippet de request/response), y un párrafo de remediación con el objetivo de upgrade o el flag de config.
## Formatos de salida
- **Informe HTML** (por defecto): priorizado inline, listo para adjuntar a un ticket
- **SARIF 2.1.0**: tab de code-scanning en GitHub / GitLab
- **JSON**: pipe a tu SIEM
- **CycloneDX VEX**: para tracking alineado con SBOM
## Frecuencia
Un escaneo es una foto. Para servicios con despliegues frecuentes, agéndalo en CI post-deploy y en un cron semanal. El coste de un escaneo contra tu propia infra es despreciable; el coste de no saber, no.
## Falsos positivos
- La detección por versión a veces falla detrás de proxies inversos que limpian la cabecera `Server`. El escáner cae a fingerprints de comportamiento, que son más ruidosos.
- Los WAFs (Cloudflare, AWS WAF) atrapan y devuelven 403 a algunas sondas — marcado como `WAF_BLOCKED`. Re-ejecuta autenticado para saltarte el WAF y evaluar el origen.
- Los módulos TLS pueden malinterpretar endpoints que presentan distintas cipher suites por SNI.
Si te encuentras uno, abre un issue con el par request/response; el catálogo se actualiza semanalmente.
## Legal
Es tuyo cuando lo apuntas a tus cosas. No es tuyo cuando lo apuntas a las cosas de otros. El escáner se niega a correr contra dominios en su deny-list incorporado (navegadores, gobierno, bancos) sin un flag explícito `--tengo-autorizacion-escrita`, e incluso entonces escribe una entrada de audit-log en tu máquina local.
> El escaneo de vulnerabilidades es higiene, no un sustituto del diseño. El catálogo solo encuentra lo ya visto. Usa el cazador 0-day para el resto.
---