F-2026-0007·ownership-desynchronization

rotateToPublicKey only transfers Bridge ownership, leaving KeyLogRegistry desynchronized and old owner privileged

Fixedbridgecross-chainkey-registrygithub.com/pdxwebdev/yadakeyeventwallet
TL;DR

rotateToPublicKey transfers ownership only on the Bridge, leaving KeyLogRegistry owned by the old key. The old owner retains setAuthorizedCaller and upgrade rights and can later brick or upgrade the registry maliciously.

Severity
MEDIUM
Impact
HIGH
Likelihood
LOW
Method
MManual review
CAT.
Complexity
MEDIUM
Exploitability
MEDIUM
02Section · Description

Description

Both Bridge and KeyLogRegistry independently track and transfer ownership during key rotation. In the normal flow via registerKeyPairWithTransfer, both contracts transfer ownership atomically in the same transaction: KeyLogRegistry does so internally in registerKeyLogPair, and Bridge does so explicitly afterward. The same applies to upgradeWithKeyRotation.

However, rotateToPublicKey only transfers Bridge ownership and does not trigger any ownership update on KeyLogRegistry:

solidity
function rotateToPublicKey(
bytes memory existingOwnerPublicKey
) external {
// ...
feeCollector = latest.prerotatedKeyHash;
transferOwnership(latest.prerotatedKeyHash);
}

After this call, the two contracts are permanently desynchronized:

  • Bridge.owner() = new rotated address
  • KeyLogRegistry.owner() = old address

Vulnerable Scenario

  1. The owner calls rotateToPublicKey() to rotate to the next key in the pre-rotation chain.
  2. Bridge ownership transfers to the new pre-committed address.
  3. KeyLogRegistry ownership remains at the old address.
  4. The old address retains full control over KeyLogRegistry, including setAuthorizedCaller() and _authorizeUpgrade().
  5. If the old key is later compromised, the attacker can call setAuthorizedCaller(attacker) on KeyLogRegistry, revoking the Bridge's authorization and permanently bricking all key registration operations (registerKeyLog and registerKeyLogPair are onlyAuthorized).
  6. The attacker can also upgrade KeyLogRegistry to a malicious implementation via _authorizeUpgrade, since they are still its owner.

This is especially concerning given the protocol's core purpose is protection against key theft. The pre-rotation scheme guarantees that a compromised key cannot steal funds, but rotateToPublicKey breaks this guarantee for KeyLogRegistry governance.

03Section · Impact

Impact

After rotateToPublicKey, the old owner retains full administrative control over KeyLogRegistry, including the ability to revoke Bridge authorization (bricking all operations) and upgrade KeyLogRegistry to a malicious implementation. This undermines the pre-rotation security model that the protocol is built upon.

04Section · Recommendation

Recommendation

Remove rotateToPublicKey entirely and force all ownership changes through registerKeyPairWithTransfer, which keeps both contracts in sync atomically. This function is the only ownership rotation path that causes desynchronization, and it is also the source of other issues (unchecked exists return value, forced fee collector overwrite). If the function must be preserved, add a corresponding KeyLogRegistry ownership transfer:

solidity
function rotateToPublicKey(
bytes memory existingOwnerPublicKey
) external {
// ... existing checks ...
(KeyLogEntry memory latest, bool exists) =
keyLogRegistry.getLatestChainEntry(existingOwnerPublicKey);
require(exists, "Key log not initialized");
feeCollector = latest.prerotatedKeyHash;
transferOwnership(latest.prerotatedKeyHash);
// Sync KeyLogRegistry ownership
keyLogRegistry.transferOwnership(latest.prerotatedKeyHash);
}
05Section · Resolution

Resolution

YadaCoin, Confirmed. Added KeyLogRegistry ownership synchronization in rotateToPublicKey and introduced a transferOwnershipForKeyRotation wrapper to handle atomic ownership transfer across both contracts.

Zealynx, Fixed. Verified that ownership is now transferred atomically to both Bridge and KeyLogRegistry, preventing the desynchronization vector.

Status
Fixed
F-2026-0007

oog
zealynx

Smart Contract Security Digest

Monthly exploit breakdowns, audit checklists, and DeFi security research — straight to your inbox

© 2026 Zealynx