Project

One World
NFTDeFi
15,000 USDC
View results
Submission Details
Severity: medium
Invalid

EIP-712 Message Hash Computation Deviation

Github

Summary

The toTypedMessageHash function accepts a pre-computed messageHash parameter instead of computing hashStruct(message) as required by the EIP-712 specification. This deviation from the standard could lead to signature verification issues and potential security vulnerabilities.

Vulnerability Details

Current Implementation:

function toTypedMessageHash(bytes32 messageHash)
internal
view
returns (bytes32)
{
return
keccak256(
abi.encodePacked("\x19\x01", getDomainSeperator(), messageHash)
);
}

According to EIP-712:

  • The final encoding should be: "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)

  • Where hashStruct(message) is defined as: keccak256(typeHash ‖ encodeData(message))

  • The function currently accepts an arbitrary messageHash without enforcing proper struct hashing

The implementation takes a shortcut by accepting a pre-computed hash instead of implementing the full EIP-712 structured data hashing mechanism. This violates the standard's requirement for type-safe hashing and proper domain separation.
The EIP-712 standard requires specific structured data handling where:

  • Each struct type has a defined typeHash

  • Struct data is encoded according to specific rules

  • The hashStruct function combines these in a standardized way

The current implementation bypasses these requirements by accepting any arbitrary hash, potentially breaking the type-safety guarantees that EIP-712 provides.

// EIP-712 Correct Implementation
struct Mail {
address from;
address to;
string contents;
}
// How it should be implemented
bytes32 constant MAIL_TYPEHASH = keccak256("Mail(address from,address to,string contents)");
function hashStruct(Mail memory mail) public pure returns (bytes32) {
return keccak256(abi.encode(
MAIL_TYPEHASH,
mail.from,
mail.to,
keccak256(bytes(mail.contents))
));
}
// Current problematic usage
bytes32 arbitraryHash = keccak256("anything");
bytes32 result = toTypedMessageHash(arbitraryHash); // Accepts any hash without type checking

Impact

  • Breaks EIP-712's type-safety guarantees

  • No guarantee that the provided hash represents a properly encoded struct

  • May lead to signature verification issues across different implementations

  • Potential for signature malleability if hashes aren't properly structured

Tools Used

Manual Review

Recommendations

Implement hashStruct in the code too i.e:

struct EIP712Message {
bytes32 typeHash;
bytes encodedData;
}
function hashStruct(EIP712Message memory message) internal pure returns (bytes32) {
return keccak256(abi.encode(
message.typeHash,
message.encodedData
));
}
function toTypedMessageHash(EIP712Message memory message)
internal
view
returns (bytes32)
{
bytes32 structHash = hashStruct(message);
return keccak256(
abi.encodePacked("\x19\x01", getDomainSeparator(), structHash)
);
}
Updates

Lead Judging Commences

0xbrivan2 Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Appeal created

0xbeastboy Submitter
6 months ago
0xbrivan2 Lead Judge
6 months ago
0xbrivan2 Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.