Skip to content

Feature/receive pubkey shares - #28

Merged
evgeny-stakewise merged 10 commits into
mainfrom
feature/receive-pubkey-shares
Jun 19, 2026
Merged

Feature/receive pubkey shares#28
evgeny-stakewise merged 10 commits into
mainfrom
feature/receive-pubkey-shares

Conversation

@evgeny-stakewise

@evgeny-stakewise evgeny-stakewise commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Motivation

A security report (Immunefi #81970) identified three weaknesses in POST /exit-signature:

  1. BLS validation was length-only. validate_bls_signature accepted any 96-byte hex string with no curve membership or subgroup check, making it trivial to submit garbage that passes validation.

  2. First-write-wins with no overwrite path. Once a share was stored at a given (validator_pubkey, share_index), any subsequent submission was silently discarded. An attacker who filled every share slot before legitimate DVT operators submitted would permanently block that validator from exiting — with no recovery path short of a service restart (and the attack repeats immediately after restart).

  3. Unhandled exception on reconstruction with no state cleanup. When garbage shares accumulated to the threshold, reconstruct_shared_bls_signature would throw a ValueError from py_ecc (invalid G2 point), propagating as HTTP 500 with the corrupt shares still in memory. There was no way to clear the slot and try again.

Changes

Cryptographic validation before storage (src/validators/schema.py)

SignatureShareRequest now requires callers to submit all operators' public key shares alongside their signature share. A model_validator runs before anything is stored and rejects the request (HTTP 422) unless:

  • the submitted public key shares reconstruct to the registered validator's full public key;
  • the exit signature share verifies against the operator's public key share at share_index;
  • the deposit signature share verifies against the operator's public key share at share_index.

Because validation happens at the request boundary, only cryptographically valid shares ever reach storage. First-write-wins remains the storage model — but it is now safe, since no garbage can enter.

State cleanup on reconstruction failure (src/validators/endpoints.py)

_reconstruct_exit_signature and _reconstruct_deposit_signature wrap the reconstruction and validation step in a try/except that calls .clear() on the shares dict and returns HTTP 400 on any failure. This prevents a corrupt state from becoming permanent even if an invalid share somehow bypassed the earlier check.

BLS public key share helpers (src/validators/key_shares.py, src/validators/exit_signature.py)

Added reconstruct_shared_bls_public_key and the share-level validators validate_public_key_shares, validate_exit_signature_share, validate_deposit_signature_share used by the schema validator above.

@evgeny-stakewise
evgeny-stakewise merged commit 1a0c264 into main Jun 19, 2026
5 checks passed
@evgeny-stakewise
evgeny-stakewise deleted the feature/receive-pubkey-shares branch June 19, 2026 11:58
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.

2 participants