Feb 12, 2026·5 min read·64 visits
Libcrux, a high-assurance crypto library, suffered from three distinct flaws: 1) An unauthenticated remote DoS caused by panicking on failed decryption, 2) Incorrect Ed25519 key derivation (clamping the seed instead of the scalar), and 3) Broken X25519 validation logic. Patches were released in Feb 2026.
In the world of cryptography, 'High Assurance' and 'Formally Verified' are the golden tickets. They promise mathematical certainty that code does exactly what it's supposed to do. But in February 2026, the `libcrux` library—a poster child for this movement—demonstrated that you can prove a theorem but still crash a server. This report details a trifecta of failures: a remotely triggerable Denial of Service via a Rust `unwrap()`, a cryptographic clamping error that rendered keys incompatible with the rest of the world, and a validation check so logically broken it rejected its own valid keys.
There is a delicious irony in finding basic implementation bugs in software marketed as 'High Assurance.' libcrux is designed to be the Fort Knox of cryptography, using formal verification to prove correctness. It’s the kind of library you use when you don't trust standard OpenSSL implementations. But verification often proves that the code matches a specification, not that the specification—or the glue code around it—is sane.
In early 2026, researchers at Symbolic Software popped the hood on libcrux and found three glaring issues that range from 'cryptographically annoying' to 'operationally fatal.' The most embarrassing? A classic Rust newbie mistake: handling fallible operations with a panic.
While the cryptographic math might have been sound in a vacuum, the implementation committed the cardinal sin of systems programming: assuming inputs will always be well-formed. This report dissects how a library built for safety left the door open for a cheap Denial of Service and generated keys that looked correct but were mathematically isolated from the rest of the RFC-compliant world.
This vulnerability isn't one bug; it's a trench coat containing three smaller bugs standing on each other's shoulders.
1. The Panic Button (DoS):
In libcrux-psq, the library implemented AEAD (Authenticated Encryption with Associated Data). When decrypting data, the library must verify the authentication tag. If the tag is wrong (spoofed or corrupted), decryption fails. In Rust, you handle this with a Result. libcrux decided to handle it with .unwrap(). This means if an attacker sends any garbage data, the decryption returns an error, .unwrap() panics, and the entire application crashes. Unauthenticated, remote, instant kill.
2. The Clamping Confusion (Ed25519):
Curve25519 requires 'clamping'—setting specific bits to ensure security properties. RFC 8032 says: take the 32-byte seed, SHA-512 hash it, then clamp the resulting scalar. libcrux got eager and clamped the seed before hashing it. This reduced the entropy of the seed (wasting 5 bits) and, critically, generated public keys that no other standard library (Sodium, OpenSSL, etc.) could reproduce from the same seed. It’s like speaking a dialect of English that only you understand.
3. The Impossible Check (X25519):
The library tried to validate imported keys with the logic: if value[31] & 64 != 1. Let's do the math. value[31] is a byte. 64 is 0x40 (binary 01000000). A bitwise AND between any byte and 64 results in either 0 or 64. Neither of those is 1. The condition is logically nonsensical, leading to valid keys being rejected or accepted based on flawed premises.
Let's look at the diffs, because they paint a vivid picture of the oversight.
The DoS (AEAD Unwrap):
Here is the culprit in src/aead.rs. The code was unwrapping the result of the decryption. If the ciphertext was tampered with, decrypt returns an Err, and unwrap sends the process to the graveyard.
// BEFORE (Vulnerable)
let plaintext = aead::decrypt(key, nonce, associated_data, ciphertext)
.unwrap(); // <--- The self-destruct button
// AFTER (Fixed)
let plaintext = aead::decrypt(key, nonce, associated_data, ciphertext)
.map_err(|_| Error::DecryptionFailed)?; // Propagate the error safelyThe Logical Falacy (X25519 Check):
The validation logic in libcrux-psq was trying to enforce bit settings but failed basic boolean logic.
// BEFORE (Broken Logic)
// 64 is 0x40. (x & 0x40) is either 0 or 64. It is never 1.
if value[31] & 64 != 1 {
return Err(Error::InvalidKey);
}
// AFTER (Corrected)
// Checks if the 6th bit is set (which is 0x40)
if value[31] & 0x40 == 0 {
return Err(Error::InvalidKey);
}The Clamping Mix-up: The fix involved moving the bit-clearing operations to happen after the SHA-512 expansion, aligning the implementation with RFC 8032.
Exploiting the encryption flaws (clamping) is subtle and mostly results in broken interoperability. However, exploiting the AEAD panic is trivial and devastating for availability.
Target: A server using libcrux-psq to handle encrypted sessions (e.g., a secure handshake or VPN termination).
Attack Chain:
libcrux (perhaps via banner grabbing or specific protocol behavior).aead::decrypt().Err..unwrap() on the Err.This is an asymmetric attack. It costs the attacker one packet and zero CPU to crash a high-assurance system that might take seconds or minutes to restart.
The remediation is straightforward: upgrade the library. The flaws are implementation defects, not protocol weaknesses, so no configuration change will save you. You must replace the binary.
Steps:
libcrux crates to the latest version (released post-Feb 9, 2026).unwrap() calls on cryptographic operations. This pattern is a plague. Use clippy or custom linting rules to ban .unwrap() in production network paths.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
libcrux-ed25519 Cryspen | < Feb 2026 Patch | Commit 4d6f5d3 |
libcrux-psq Cryspen | < Feb 2026 Patch | Commit f303b64 |
| Attribute | Detail |
|---|---|
| CWE-248 | Uncaught Exception (Panic) |
| CWE-326 | Inadequate Encryption Strength (Entropy Loss) |
| CWE-20 | Improper Input Validation |
| CVSS | 7.5 (High) |
| Attack Vector | Network (Unauthenticated) |
| Impact | Denial of Service & Interoperability Failure |
An uncontrolled resource consumption vulnerability exists in the Scala-based http4s-blaze-server package of the http4s/blaze library. The vulnerability allows remote, unauthenticated attackers to cause an Out of Memory Error (OOM) and JVM crash by streaming a continuous sequence of small or empty WebSocket continuation frames with the FIN bit set to 0. This bypasses typical payload size checks because of the JVM's per-object allocation overhead, leading to rapid heap exhaustion with minimal network bandwidth.
A critical path traversal vulnerability has been identified in the OpenList Go-based backend package. The vulnerability exists within the batch rename handler because the application does not validate the source filename parameter before constructing filesystems paths. This omission allows authenticated users to escape their designated directory and rename files in sibling paths.
OpenList version 4.2.3 and prior is vulnerable to an authorization bypass and metadata leakage. When configured with the Bleve search engine backend, OpenList fails to perform separator-aware path matching when validating tenant containment. This allows authenticated users to access sibling directories sharing similar name prefixes. Furthermore, the search backend returns unfiltered global result counts, leaking existence verification data of unauthorized files via side-channel analysis.
An authorization bypass vulnerability in OpenList version 4.2.3 and below allows authenticated users to read arbitrary files outside of their designated base directories due to an insecure path prefix check using Go's standard strings.HasPrefix function.
A security policy bypass vulnerability exists in the AWS API MCP Server (awslabs-aws-api-mcp-server) from version 0.2.13 through 1.3.46. When the server fails to load the read-only operations index during startup (due to transient network failures, file permission issues, or other exceptions), it logs a warning but continues running in an insecure, degraded state. Under this condition, the security policy engine fails open, silently skipping all subsequent security checks and consent prompts for the lifetime of the process. This permits unauthorized mutating AWS CLI commands to execute via indirect prompt injection attacks.
An incomplete escaping vulnerability in the npm package 'shescape' allows unauthenticated users to trigger dynamic shell expansions, absolute path disclosure, and command block break-outs on Unix and Windows systems.