Repository navigation
fix(stellar-announcer): reject invalid ed25519 ephemeral public keys - #242
Conversation
|
@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! 🚀 |
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.
a8ae2f5 to
d09b1dc
Compare
truthixify
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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.
Summary
Resolves audit finding WA-ANN-03:
announceaccepted any 32-byte value asephemeral_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.rsrejects:y >= p),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'scrypto_core_ed25519_is_valid_pointrather than only special-casing the all-zero value. The module is self-contained (GF(2^255 - 19)arithmetic over fouru64limbs) so the deliberately frozen, minimal announcer does not take a new curve-library dependency.Tests and fixtures
Contract fixtures
stealth-announcer/tests/audit.rs: flipswa_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: addstest_conformance_announcer_rejects_{zero,non_canonical,off_curve}_ephemeral_pub_keyalongside the existing canonical fixture.Harness updates
[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 report0 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)announce's signature and event shape are unchanged.Closes #226