Front-running the encryption-key--registration transaction can deny service to function finalizeBundle
Description
Each encryption key is associated with a message index, meaning that this encryption key will be used starting from that message index. The first encryption key will be registered after the contract is deployed, meaning the key registration and the contract deployment are two separate transactions.
function registerNewEncryptionKey(bytes memory _key) external onlyRole(KEY_MANAGER_ROLE) {
if (_key.length != 33) revert ErrorInvalidEncryptionKeyLength();
uint256 _keyId = encryptionKeys.length;
// The message from `nextCrossDomainMessageIndex` will utilise the newly registered encryption key.
uint256 _msgIndex = IL1MessageQueueV2(messageQueueV2).nextCrossDomainMessageIndex();
encryptionKeys.push(EncryptionKey(_msgIndex, _key));
emit NewEncryptionKey(_keyId, _msgIndex, _key);
}Impact
Malicious users can queue any cross-chain messages before the encryption-key--registration transaction, causing the return value of the nextCrossDomainMessageIndex function to be greater than zero.
When finalizing the bundle, the encryption key for message index 0 will be retrieved. Since the first encryption key is associated with a message index that is not zero, the _getEncryptionKey function will throw the ErrorUnknownEncryptionKey error, causing the bundle to fail finalization.
function _finalizeBundle(
bytes calldata batchHeader,
uint256 totalL1MessagesPoppedOverall,
bytes calldata aggrProof
) internal virtual {
// [...]
// Get the encryption key at the time of on-chain message queue index.
bytes memory encryptionKey = totalL1MessagesPoppedOverall == 0
? _getEncryptionKey(0)
: _getEncryptionKey(totalL1MessagesPoppedOverall - 1);
// [...]
}
function _getEncryptionKey(uint256 _msgIndex) internal view returns (bytes memory) {
// [...]
while (_encryptionKey.msgIndex > _msgIndex) {
if (_numKeys == 0) revert ErrorUnknownEncryptionKey();
_encryptionKey = encryptionKeys[--_numKeys];
}
return _encryptionKey.key;
}Recommendations
Consider deploying the messenger after registering the first encryption key to prevent others from sending cross-chain messages or registering the first encryption key during the contract initialization.