Following Part 1, in this article we focus on guardian signatures verification in Wormhole on both Solana and Ethereum.
On Solana, Wormhole uses the verify_signatures function to verify all the signatures in a VAA. Each VAA may contain multiple signatures (at least 2/3 of 19 verified signatures to reach a quorum). Because of the compute limit, it splits the signature verification into multiple steps (i.e., calling verify_signatures multiple times), with each call verifying a subset (e.g., six or seven) of the guardian signatures:
The input accounts in VerifySignatures are defined below:
The two PDA accounts guardian_set and signature_set are important. The guardian_set account must have been initialized (AccountState::Initialized) and it stores a set of the verified guardians (including their keys):
The signature_set stores the verified status (true or false) of each guardian signature (signatures: Vec<bool>), the message hash, and the guardian_set_index:
The signature_set account is created and initialized in the first call to verify_signatures:
In the subsequent calls, the same signature_set is validated with its corresponding message hash and guardian_set_index to ensure it cannot be faked (lines 188 and 192 above).
Once a signature is verified, the corresponding signer_index of signature_set.signatures will be set true:
Wormhole (on Solana) uses the Precompiled Secp256k1 SigVerify Program and each call to verify_signatures is prepended with an instruction to he SigVerify program, which verifies an input sequence of signatures:
Wormhole uses sysvar_instructions to load the current_instruction corresponding to verify_signatures and it checks that its previous instruction must be a secp verification instruction (i.e., a call to SigVerify):
The program instruction logs illustrate the flow clearly:
Different from Solana, Wormhole on Ethereum verifies all signatures in a single transaction by calling the verifyVM function (vm is a VAA byte array):
It ensures quorum by checking the signatures length:
It calls verifySignatures (similar to Solana, taking also message hash and guardianSet as input):
The verifySignatures verifies the input signatures on by one by using ecrecover (line 93) :
The ecrecover(message, v, r, s) API computes the key that produced a signature (v, r, s) on message. See here for a technical description of ecrecover. If verification passes if the returned key matches with the corresponding guardian’s public key (guardianSet.keys[sig.guardianIndex]).
Wormhole prevents this attack surfaces by ensuring that the signature indices in the VAA must be ascending (on Ethereum):
On Solana, Wormhole maintains a pair (sig_index, signer_index) for each signature, and matches the key of the verified signature with the corresponding guardian public key (line 209):
Even if an attacker were able to control a guardian and place 19 repeated signatures in an VAA, only one guardian's signer_index would be set to true in signature_set.signature, thus the attack would fail.
Wormhole also checks validity of the guardian_set and integrity of the signature_set:
The quorum check is done in the PostVAA instruction:
In the next article, we will continue to discuss the security of cross-chain token mint/burn in Wormhole.
sec3 is a security research firm that prepares blockchain projects for millions of users. sec3’s Launch Audit is a rigorous, researcher-led code examination that investigates and certifies mainnet-grade smart contracts; sec3’s continuous auditing software platform, X-ray, integrates with Github to progressively scan pull requests, helping projects fortify code before deployment; and sec3’s post-deployment security solution, WatchTower, ensures funds stay safe. sec3 is building technology-based scalable solutions for Web3 projects to ensure protocols stay safe as they scale.
To learn more about sec3, please visit https://www.sec3.dev