Skip to content

Fixed: device wallet front-running random eSIM identifier - #117

Merged
ManulParihar merged 10 commits into
mainfrom
dev
Aug 18, 2026
Merged

ManulParihar merged 10 commits into
mainfrom
dev

Conversation

@ManulParihar

@ManulParihar ManulParihar commented Aug 18, 2026 •

Copy link
Copy Markdown
Member

Updates:

  • Reorganised configurations in certora folder
  • Updated README
  • Added deployment rehearsal script against a local fork using anvil

Bug Fix: Move eSIM identifier assignment to the registry

What changed

  • Registry.claimESIMIdentifier removed. Registry.assignESIMIdentifier(eSIMWallet, identifier) replaces it, gated on onlyESIMWalletAdmin.
  • DeviceWallet.setESIMUniqueIdentifierForAnESIMWallet and its onlyESIMWalletAdminOrRegistry modifier deleted. Device wallets no longer touch identifiers.
  • ESIMWallet.setESIMUniqueIdentifier moved from onlyDeviceWallet to onlyRegistry.
  • Claim and write now happen in one internal function, RegistryHelper._assignESIMIdentifier. The lazy deployment path calls the same one.

The attack this closes

  • Which identifier a wallet is owed is decided offchain at purchase. The chain learns it only when the admin sends the assignment, and that transaction sits in the public mempool first.
  • The old claimESIMIdentifier accepted a call from any registered device wallet. Its only check was that the eSIM wallet named in the call belonged to the caller.
  • So an attacker deploys an eSIM wallet of their own, reads the victim's pending assignment, and calls claimESIMIdentifier(victimIdentifier, attackerOwnedWallet) with more gas. The ownership check passes, because they really do own the wallet they named.
  • claimedESIMIdentifiers is never cleared. The victim's assignment then reverts with ESIMIdentifierAlreadyClaimed permanently, and the eSIM they paid for cannot be delivered.
  • No onchain check fixes this: every fact a device wallet offers about its own wallets is one it wrote itself. Only the admin knows the correct pairing.

Coverage

  • NemesisIdentifierSquatPoC.t.sol replays the original attack and shows it reverting on the first line.
  • IdentifierCollision.t.sol and the device wallet guard tests now drive registry.assignESIMIdentifier.

ManulParihar and others added 10 commits August 15, 2026 15:37
The identifier was written by the owning device wallet, which let an owner
set a string the registry had no record of. Assigning and claiming now happen
together in one admin-only path, so the two slots cannot disagree.

setESIMUniqueIdentifier is onlyRegistry, and the device wallet no longer
carries the setter or the modifier that guarded it.
full/ verifies a whole spec, scoped/ narrows to a few rules where the full run
is too slow or needs a different bound, probe/ is for one-off checks.
Notes on the vacuous records the logs carry: postCreateAccount is unreachable
because the prover gives code only to contracts in the scene, not because of
the hash bound, so the duplicate report guard is unproved and covered by the
unit tests instead. The registry setter vacuity in ESIMWalletFactory is
expected, since the rule needs a state the setter refuses.
@ManulParihar
ManulParihar merged commit 8e49dd9 into main Aug 18, 2026
1 check 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.

1 participant