Skip to content

fix(security): bind validator PoP message to chain/contract and enforce canonical low-s ECDSA - #4

Closed
mertcano wants to merge 1 commit into
chronicleprotocol:mainfrom
mertcano:mertcano-patch-1
Closed

mertcano wants to merge 1 commit into
chronicleprotocol:mainfrom
mertcano:mertcano-patch-1

Conversation

@mertcano

Copy link
Copy Markdown

Summary of Changes

Resolves Finding 3 (Medium) and Finding 4 (Low) from the Chronicle workspace security audit in ValidatorRegistry.sol:

  1. Binds constructValidatorRegistrationMessageV2 to address(this) and block.chainid to prevent cross-registry and cross-chain Proof-of-Possession (PoP) signature replay[cite: 28, 33].
  2. Enforces canonical low-s check in _lift to eliminate ECDSA signature malleability and protects occupied validator slots with explicit error messaging[cite: 28, 33].

Vulnerability Analysis

  • PoP Replay across Chains / Registries (Finding 3):

    • Without contract and chain domain separation, a validator registration signature was entirely deterministic based on the public key coordinates alone.
    • An observer capturing a registration signature on Chain A could replay the same byte payload to register the validator on Chain B without holding the corresponding private key at registration time.
  • Signature Malleability & Slot Collisions (Finding 4):

    • High-s values (s > secp256k1n / 2) permitted two distinct valid on-chain encodings for the same registration signature, violating byte-uniqueness assumptions.
    • Missing guards on occupied IDs risked silent evictions of active validators[cite: 33].

Key Remediations

  • Domain Separation Enforcement (constructValidatorRegistrationMessageV2): ```solidity return keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", keccak256( abi.encodePacked( address(this), block.chainid, _CONTEXT, pubKey.x, pubKey.y ) ) ) );

Canonical Low-s Assertion (_lift):

require(
feedRegistrationSigV2.s
<= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
"ValidatorRegistry: signature s not canonical"
);

ID Collision Guard: Added require(current == address(0), "ValidatorRegistry: id occupied"); to prevent overwriting active validators[cite: 33].

Verification Compiled with solc 0.8.24 (and 0.8.16 base): clean compilation with zero warnings. Verified rejection of high-s inverted signatures.Verified domain mismatch rejection between differing chainid or contract instances.

…ce canonical low-s ECDSA

### Summary of Changes
Resolves **Finding 3 (Medium)** and **Finding 4 (Low)** from the Chronicle workspace security audit in `ValidatorRegistry.sol`:
1. Binds `constructValidatorRegistrationMessageV2` to `address(this)` and `block.chainid` to prevent cross-registry and cross-chain Proof-of-Possession (PoP) signature replay[cite: 28, 33].
2. Enforces canonical low-s check in `_lift` to eliminate ECDSA signature malleability and protects occupied validator slots with explicit error messaging[cite: 28, 33].

---

### Vulnerability Analysis

- **PoP Replay across Chains / Registries (Finding 3):**
  - Without contract and chain domain separation, a validator registration signature was entirely deterministic based on the public key coordinates alone.
  - An observer capturing a registration signature on Chain A could replay the same byte payload to register the validator on Chain B without holding the corresponding private key at registration time.

- **Signature Malleability & Slot Collisions (Finding 4):**
  - High-s values (`s > secp256k1n / 2`) permitted two distinct valid on-chain encodings for the same registration signature, violating byte-uniqueness assumptions.
  - Missing guards on occupied IDs risked silent evictions of active validators[cite: 33].

---

### Key Remediations

- **Domain Separation Enforcement (`constructValidatorRegistrationMessageV2`):**
  ```solidity
  return keccak256(
      abi.encodePacked(
          "\x19Ethereum Signed Message:\n32",
          keccak256(
              abi.encodePacked(
                  address(this),
                  block.chainid,
                  _CONTEXT,
                  pubKey.x,
                  pubKey.y
              )
          )
      )
  );
@pmerkleplant

Copy link
Copy Markdown
Member

Risk accepted and/or simply not applicable. Next time, please open an issue first to discuss. For security relevant issues, send an email to security@chroniclelabs.org.

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