Skip to content

RustSec Advisories for hpke-rs and libcrux-psq#2647

Open
nadimkobeissi wants to merge 9 commits intorustsec:mainfrom
nadimkobeissi:cryspen-vulns
Open

RustSec Advisories for hpke-rs and libcrux-psq#2647
nadimkobeissi wants to merge 9 commits intorustsec:mainfrom
nadimkobeissi:cryspen-vulns

Conversation

@nadimkobeissi
Copy link

@nadimkobeissi nadimkobeissi commented Feb 16, 2026

This PR proposes three RustSec advisories for cryptographic vulnerabilities discovered during research ("Verification Theatre: False Assurance in Formally Verified Cryptographic Libraries"). The upstream maintainers (Cryspen) have patched these issues but have not filed RustSec advisories, which means cargo audit does not currently flag affected versions.

Cryspen has published GitHub Security Advisories (GHSAs) for some of these issues. However, we believe RustSec advisories are still warranted for the reasons outlined below.

Proposed Advisories

1. hpke-rs: Nonce reuse via sequence number overflow

The library stores HPKE encryption context sequence numbers as u32 (max 2^32 - 1) rather than enforcing the RFC 9180 limit of 2^96 - 1. The overflow guard is ineffective in release builds due to Rust's default wrapping behavior for integer overflow, allowing silent counter wrap-around. This enables nonce reuse in AES-GCM, which permits plaintext recovery and authentication key compromise, a critical vulnerability.

2. hpke-rs: Missing X25519 zero-check validation

RFC 9180, Section 7.1.4 requires that implementations check whether the Diffie-Hellman shared secret is the all-zero value. hpke-rs omits this check in both its RustCrypto and libcrux backends. An attacker supplying low-order points can force a zero shared secret, enabling session key prediction and message decryption.

3. libcrux-psq: AES-GCM decryption panic on malformed ciphertext

The decrypt_out method calls .unwrap() on AES-GCM 128 decryption results, causing a process crash when presented with malformed ciphertexts. Nine of eighteen supported ciphersuites are affected. An unauthenticated attacker can trigger denial of service by sending corrupted data, rendering the entire implementation not IND-CCA secure, a critical vulnerability.

Why RustSec advisories are needed alongside the existing GHSAs

Cryspen published GHSA-g433-pq76-6cmf for hpke-rs and GHSA-435g-fcv3-8j26 for libcrux-psq. While we appreciate that an advisory was issued, there are several gaps that leave downstream users without the information they need:

  • Severity classification: The hpke-rs GHSA rates the nonce-reuse vulnerability as "moderate." Nonce reuse in AES-GCM enables full plaintext recovery and authentication key compromise, which is generally considered a critical or high-severity issue in cryptographic contexts. Similarly, the libcrux-psq GHSA rates the IND-CCA-breaking bug as "moderate" only.
  • Missing impact analysis: The GHSAs does not include a details or impact section explaining what an attacker can achieve by exploiting these vulnerabilities, making it difficult for downstream consumers to assess their risk.
  • Bundled vulnerabilities: Multiple distinct vulnerabilities with different root causes and impacts are grouped into a single advisory, which makes it harder for users to evaluate which issues affect their use case.
  • No RustSec coverage: The GHSAs are not mirrored in RustSec, so cargo audit — the standard tool Rust developers rely on to check for known vulnerabilities — does not surface these issues.

Prior discussion

These advisories were previously proposed in PR #2637, which was closed. We sought an explanation in #2646. We are resubmitting with the hope of a productive path forward. We are happy to incorporate feedback on advisory wording, severity ratings, or any other aspect of these filings, and to work with both RustSec reviewers and the upstream maintainers to ensure the advisories are accurate and fair.

Thank you for your time and for maintaining this important resource for the Rust ecosystem.

@@ -0,0 +1,23 @@
```toml
[advisory]
id = "RUSTSEC-0000-0000"
Copy link
Contributor

@pinkforest pinkforest Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can only submit one advisory per crate in a PR with id RUSTSEC-0000-0000

The lint check fails because it doesn't expect 000-0001.md

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple advisories for one crate in a single PR are possible.
The filename needs to be RUSTSEC-0000-0000.<N>.md where <N> is replaced with a number see https://github.com/rustsec/advisory-db/pull/2254/changes for example

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes correct my bad

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Skgland Thanks for this, I had no idea. I'll push a fix now.

nadimkobeissi and others added 2 commits February 16, 2026 16:33
Co-authored-by: pinkforest(she/her) <36498018+pinkforest@users.noreply.github.com>
Copy link
Contributor

@teor2345 teor2345 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some suggestions for consistency, that apply similarly to all 3 advisories.

references = ["https://datatracker.ietf.org/doc/rfc9180/"]

[versions]
patched = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This empty "patched" entry is inconsistent with the text below saying the library has been patched.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think he fixed already one of them, I also should have said ">= 0.6.0" instead of "> 0.6.0"

id = "RUSTSEC-0000-0000"
package = "hpke-rs"
date = "2026-02-10"
url = "https://github.com/cryspen/hpke-rs/pull/117"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually this would point to the fix that was actually merged, see below for a suggestion referencing the original disclosure.

[versions]
patched = []
```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand their SECURITY.md is very new, but it says:

hpke-rs is still pre-release and does not support any specific versions yet.

Does this impact the severity of this issue, or justify wording something like:

Suggested change
hpke-rs is currently pre-release, and [not supported for use in production code](https://github.com/cryspen/libcrux/blob/b7187c5b115f64bf98830b7ac7980d68cbe295d6/SECURITY.md?plain=1#L5).

Copy link
Contributor

@pinkforest pinkforest Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's published in crates.io so it's covered. Severity isn't affected by things like this but it can be a mention in the advisory itself to ensure tools like cargo audit etc. traps advising that hey you should not use this in production etc. if that type of statement is still left there where people may have missed it when using the library.

Also it's important to keep in mind that when ever people started using the library they might or might not have read the said disclaimer so it should be mentioned in the advisory probably to ensure people understand the status of the library. If this disclaimer would have been from the beginning that would be another but since this is new I would include this info to make sure people understand the status.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cryspen has actively lobbied for their “pre-release” software to be used in production in Signal, Google and OpenMLS, where it is today, and I think that speaks way more than a 0.0.x version number.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can see the Signal and OpenMLS dependencies here, I assume the Google ones are private (or transitive):
https://crates.io/crates/hpke-rs/reverse_dependencies

Copy link
Contributor

@pinkforest pinkforest Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signal does not publish their crates in crates.io - the one signal there is a vendored-in fork who decided to publish it. for advisory-db it hasn't mattered whether there is dependency visible in reverse but only that it is published in crates.io

nadimkobeissi and others added 4 commits February 17, 2026 08:36
Co-authored-by: pinkforest(she/her) <36498018+pinkforest@users.noreply.github.com>
Co-authored-by: teor <teor@riseup.net>
references = ["https://datatracker.ietf.org/doc/rfc9180/"]

[versions]
patched = [">0.6.0"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using >= for patched is clearer

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed for all advisories.

@CodesInChaos
Copy link

An attacker who can supply a malicious public key containing a low-order point can force the shared secret to zero, making the HPKE key schedule deterministic and predictable. The attacker can then compute the same session keys as the sender and decrypt all messages.

Can't the attacker compute the session key just as easily when they supply a normal public key for which they know the corresponding private key? So it's not clear to me this has a practical security impact, or if it's just non compliance with the specification.

An unauthenticated attacker can trigger denial of service by sending corrupted data, rendering the entire implementation not IND-CCA secure, a critical vulnerability.

The claim that an unauthenticated crash vulnerability (which is definitely bad) renders the application not IND-CCA secure because it can't respond to further queries is a rather creative interpretation of IND-CCA.

@nadimkobeissi
Copy link
Author

The claim that an unauthenticated crash vulnerability (which is definitely bad) renders the application not IND-CCA secure because it can't respond to further queries is a rather creative interpretation of IND-CCA.

I don't understand. It's a classic distinguisher.

@nadimkobeissi
Copy link
Author

Advisories have been merged in #2667 for some of the issues here, but notably not for the hpke-rs issues which include catastrophic nonce reuse.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants