Assessment reports>Hyperlane Starknet>Critical findings>Dynamic variable size for hash parameters
Category: Coding Mistakes

Dynamic variable size for hash parameters

Critical Severity
Critical Impact
High Likelihood

Description

The digest and domain_hash functions in checkpoint_lib.cairo, the format_message function in message.cairo, and the domain_hash function in validator_announce.cairo try to follow the behavior of abi.encodePacked by appending the ByteData struct with the size calculated with the u{64,256}_word_size:

fn format_message(_message: Message) -> (u256, Message) {
    let sender: felt252 = _message.sender.into();
    let recipient: felt252 = _message.recipient.into();

    let mut input: Array<ByteData> = array![
        ByteData {
            value: _message.version.into(), size: u64_word_size(_message.version.into()).into()
        },
        ByteData {
            value: _message.nonce.into(), size: u64_word_size(_message.nonce.into()).into()
        },
        ByteData {
            value: _message.origin.into(), size: u64_word_size(_message.origin.into()).into()
        },
        ByteData { value: sender.into(), size: ADDRESS_SIZE },
        ByteData {
            value: _message.destination.into(),
            size: u64_word_size(_message.destination.into()).into()
        },
        ByteData { value: recipient.into(), size: ADDRESS_SIZE },
        ByteData {
            value: _message.body.size().into(),
            size: u64_word_size(_message.body.size().into()).into()
        },
    ];

    // ...
}

However, static type variables such as uint32, uint256, and bytes32 take up the space of the result of the abi.encodePacked function as much as its static size, like abi.encodePacked(bytes4(uint32(1)), bytes4(uint32(2))) returns 0x0000000100000002, not 0x0102.

Since u{64,256}_word_size returns the minimum byte length to store the given value (e.g., u64_word_size(1_u64) == 1_u8), using these functions to encode the static type variables and implement the abi.encodePacked function may result in incorrect behavior for inputs that start with the zero byte.

In the case where the 128-bit chunk of the body starts with the zero bytes, for instance, if the chunk of the body looks like 0x000...(total 16 bytes)...001, the word size function will return one and it will be interpreted as 0x01 when it is hashed.

Impact

This bug may result in generating messages incompatible to the ABI of the Hyperlane protocol, which may lead to the failure of message dispatching and processing.

Recommendations

Consider using the static size instead of dynamically calculated size when generating messages.

Remediation

This issue has been acknowledged by Pragma, and fixes were implemented in the following commits:

Zellic © 2025Back to top ↑