Skip to content

fix(stellar-announcer): reject invalid ed25519 ephemeral public keys - #242

Merged
truthixify merged 2 commits into
wraith-protocol:developfrom
code3ks:fix/stellar-announcer-reject-invalid-ephemeral-keys
Oct 18, 2026
Merged

truthixify merged 2 commits into
wraith-protocol:developfrom
code3ks:fix/stellar-announcer-reject-invalid-ephemeral-keys

Conversation

@code3ks

@code3ks code3ks commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Resolves audit finding WA-ANN-03: announce accepted any 32-byte value as ephemeral_pub_key, including the all-zero key and byte strings that are not valid ed25519 point encodings.

The announcer now validates the key on-chain before emitting an event. The dependency-free check in stellar/stealth-announcer/src/ed25519.rs rejects:

  • the all-zero encoding (the concrete WA-ANN-03 reproducer),
  • non-canonical encodings (y >= p),
  • encodings that do not decompress to a curve point, and
  • the small-order points (order 1/2/4/8).

Canonical, on-curve keys — including the RFC 8032 test vectors — continue to work.

Why on-chain and dependency-free

The issue asks to reject "invalid ed25519 point encodings used by the Stellar scheme", so the check mirrors libsodium's crypto_core_ed25519_is_valid_point rather than only special-casing the all-zero value. The module is self-contained (GF(2^255 - 19) arithmetic over four u64 limbs) so the deliberately frozen, minimal announcer does not take a new curve-library dependency.

Tests and fixtures

Contract fixtures

  • stealth-announcer/tests/audit.rs: flips wa_ann_03_zero_ephemeral_pub_key_is_accepted → ..._is_rejected, and adds non-canonical / off-curve / small-order rejection cases plus a valid-key acceptance test.
  • stealth-announcer/tests/properties.rs: payload proptests now draw keys from a valid pool and add rejection properties.
  • stealth-announcer/src/ed25519.rs: unit tests covering field arithmetic, RFC 8032 keys, small-order points, non-canonical and off-curve encodings.

Cross-chain conformance fixtures

  • integration-tests/tests/conformance.rs: adds test_conformance_announcer_rejects_{zero,non_canonical,off_curve}_ephemeral_pub_key alongside the existing canonical fixture.

Harness updates

  • Real-announcer fixtures that previously used malformed placeholders (e.g. [0x02; 32], [0xff; 32]) now use valid encodings: integration-tests (chaos, topic_filter), stealth-batch-sender, stealth-vault, bench, bench-crossover.

Verification

  • cargo test -p stealth-announcer ✅
  • cargo test --workspace ✅ (all binaries report 0 failed)
  • cargo fmt --all --check ✅
  • cargo build --target wasm32-unknown-unknown --release ✅ (stealth_announcer.wasm ≈ 11.9 KB raw, far under the 110 KB gate)
  • No ABI/bindings change: announce's signature and event shape are unchanged.

Closes #226

@drips-wave

drips-wave Bot commented Oct 16, 2026

Copy link
Copy Markdown

@code3ks Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Resolve audit finding WA-ANN-03: `announce` accepted any 32-byte value as
`ephemeral_pub_key`, including the all-zero key and encodings that do not
represent valid ed25519 points.

Validate the key on-chain before emitting an event. The dependency-free
check in `src/ed25519.rs` rejects:

- the all-zero encoding,
- non-canonical encodings (`y >= p`),
- encodings that do not decompress to a curve point, and
- the small-order (order 1/2/4/8) subgroup points.

Canonical, on-curve keys, including the RFC 8032 test vectors, still work.

Fixtures: `tests/audit.rs` flips the WA-ANN-03 reproducer to a rejection and
adds non-canonical/off-curve/small-order cases plus a valid-key acceptance
test; `tests/properties.rs` draws payload keys from a valid pool and adds
rejection properties; cross-chain conformance gains rejection fixtures in
`integration-tests/tests/conformance.rs`. Existing harnesses that used
`[0x02; 32]` and other malformed placeholders now use valid encodings.
@code3ks
code3ks force-pushed the fix/stellar-announcer-reject-invalid-ephemeral-keys branch from a8ae2f5 to d09b1dc Compare October 16, 2026 02:46

@truthixify truthixify left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

stellar/stealth-announcer/src/ed25519.rs:95 still accepts on-curve points outside the prime-order subgroup. The implementation checks canonical encoding, decompression, and the eight small-order encodings, but libsodium's crypto_core_ed25519_is_valid_point additionally calls ge25519_is_on_main_subgroup (https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_core/ed25519/core_ed25519.c).

I reproduced the mismatch with a temporary differential test against curve25519-dalek: for [0x01; 32], this PR returns true, while decompress().is_torsion_free() && !is_small_order() returns false. That same repeated-byte value is currently listed as valid in this PR's tests and is used by the contract fixture. Please enforce prime-subgroup membership, preferably through a vetted implementation, and add a mixed-order regression vector. Otherwise a value that cannot be an Ed25519 public key still passes the new contract check.

Address review on wraith-protocol#242:

- Delegate ed25519 point validation to curve25519-dalek 4 (default-features = false, no_std) instead of hand-rolled curve arithmetic.

- Enforce the prime-order subgroup (is_torsion_free) alongside the canonical/on-curve/non-small-order checks, matching libsodium's crypto_core_ed25519_is_valid_point.

- Add mixed-order regression vectors, including the reviewer reproducer [0x01; 32], in the unit tests, audit/property tests and cross-chain conformance tests.

- Update real-announcer fixtures to valid prime-order keys, regenerate the affected test snapshots, and refresh the README and WA-ANN-03 remediation note.

@truthixify truthixify left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The updated validation now uses curve25519-dalek decompression plus canonical re-encoding, small-order rejection, and prime-subgroup checking, which closes the mixed-order acceptance from the prior review. Local stealth-announcer verification passed all 38 unit, audit, property, upgrade, and doc tests.

@truthixify
truthixify merged commit 3c245ab into wraith-protocol:develop Oct 18, 2026
20 checks passed
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.

[Wave 10] Reject invalid Stellar ephemeral public keys

2 participants