Skip to content

fix: validate subgroup membership in public-key deserialization (GHSA-25fp-2fjj-g84w)#51

Open
dobby-coder[bot] wants to merge 2 commits into
mainfrom
fix/subgroup-check-pubkey-deser
Open

fix: validate subgroup membership in public-key deserialization (GHSA-25fp-2fjj-g84w)#51
dobby-coder[bot] wants to merge 2 commits into
mainfrom
fix/subgroup-check-pubkey-deser

Conversation

@dobby-coder

@dobby-coder dobby-coder Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the medium-severity finding in GHSA-25fp-2fjj-g84w (tracked in #50): several public-key from_bytes implementations deserialized compressed curve points with from_compressed_unchecked, which skips the prime-order subgroup / cofactor check.

A public key received from an unauthenticated source (e.g. over a network, before integrity verification) could embed a valid on-curve point that lies outside the prime-order subgroup (BLS12-381 has a large, non-trivial cofactor). Using such a malformed key during encapsulation/encryption can draw session keys from a small subgroup, breaking IND-CCA2 guarantees. The previous inline comments justified the unchecked variant on a "trusted infrastructure" assumption that the API did not actually enforce at the boundary.

Changes

Switched to the checked from_compressed variant in every public-key from_bytes listed in the advisory, and updated the now-obsolete justification comments:

  • src/ibe/cgw.rsPublicKey::from_bytes
  • src/kem/cgw_kv.rsPublicKey::from_bytes
  • src/kem/kiltz_vahlis_one.rsPublicKey::from_bytes and HashParameters::from_bytes
  • src/ibe/boyen_waters.rsPublicKey::from_bytes

User-secret-key and ciphertext from_bytes already used the checked variant, so they are unchanged.

Tests

Added a per-scheme regression test (from_bytes_rejects_non_subgroup_point) that overwrites a G1 component of a valid serialized public key with a well-formed but non-subgroup G1 point and asserts from_bytes returns CtOption::none. The test vector is a compressed encoding of an on-curve, non-torsion-free point (from_compressed_unchecked accepts it; from_compressed rejects it), stored once as NON_SUBGROUP_G1_COMPRESSED in src/util.rs.

I confirmed the tests genuinely catch the vulnerability: temporarily reverting a single call back to from_compressed_unchecked makes the corresponding test fail.

Verification

  • cargo test --release --all-features — 31 passed, 0 failed
  • cargo clippy --all-features --all-targets — clean
  • cargo fmt -- --check — clean

Advisory: https://github.com/encryption4all/ibe/security/advisories/GHSA-25fp-2fjj-g84w
Closes #50

🤖 Generated with Claude Code

dobby-coder Bot and others added 2 commits July 4, 2026 23:19
Several `PublicKey::from_bytes` implementations (and KV1's
`HashParameters::from_bytes`) deserialized compressed curve points with
`from_compressed_unchecked`, which skips the prime-order subgroup check.
A public key received from an unauthenticated source could embed a valid
on-curve point outside the prime-order subgroup (BLS12-381 G1 cofactor is
76). Using such a key during encapsulation/encryption could draw session
keys from a small subgroup, breaking IND-CCA2 guarantees.

Switch to the checked `from_compressed` variant in the public-key
`from_bytes` implementations for CGW, CGW-KV, KV1 and Boyen-Waters, and
update the now-obsolete justification comments. Adds a regression test
per scheme that feeds a well-formed but non-subgroup G1 point and asserts
`from_bytes` rejects it.

Fixes GHSA-25fp-2fjj-g84w

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The subgroup-check rationale comments stated "BLS12-381 G1 cofactor is 76",
but the actual G1 cofactor is a 126-bit value (~7.6e37). The stray "76"
massively understated the attack surface in a security-facing comment.
Reworded to describe the large, non-trivial cofactor without a wrong number,
matching the phrasing already used in util.rs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Rules Dobby 2 — gatekeeper sign-off (posted as COMMENT only because GitHub blocks APPROVE/REQUEST_CHANGES on a self-authored PR; treat this as an approval).

Ran the full rule check against the memory ruleset and merged with Review Dobby 2's findings.

  • Rules: compliant — conventional-commit title ✅, tests present (per-scheme rejection-path regression tests) ✅, cargo fmt/build/tests green ✅ (31/31, all features), draft PR linked from the advisory ✅.
  • Review Dobby 2: 0 blocking bugs; one nit — the security-rationale comment claimed "BLS12-381 G1 cofactor is 76" in 4 files (the real G1 cofactor is a 126-bit value), and the same wrong number appeared in the PR body.

Resolved inline rather than looping (nit + trivially self-resolvable on a self-authored PR): pushed 42a9e89 correcting all four comments to describe the large, non-trivial cofactor without a wrong figure (matching util.rs), and fixed the PR body. Re-ran fmt + full suite: clean, 31/31 pass.

No outstanding findings — good to go for maintainer review.

@dobby-coder dobby-coder Bot marked this pull request as ready for review July 4, 2026 23:29
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.

Harden public key deserialization: add curve point validation

0 participants