Skip to content

Commit 315eb42

Browse files
committed
test: verify signature malleability fix with Receipt
1 parent 2badfe0 commit 315eb42

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed

tap_eip712_message/src/lib.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,114 @@ impl<M: SolStruct> Eip712SignedMessage<M> {
110110
MessageId(self.message.eip712_hash_struct().into())
111111
}
112112
}
113+
114+
#[cfg(test)]
115+
mod tests {
116+
use super::*;
117+
use thegraph_core::alloy::{
118+
primitives::{Address, Signature, U256},
119+
signers::local::PrivateKeySigner,
120+
sol_types::eip712_domain,
121+
};
122+
123+
#[test]
124+
fn test_signature_malleability_resistance() {
125+
// Create a domain separator for testing
126+
let domain_separator = eip712_domain! {
127+
name: "TAP",
128+
version: "1",
129+
chain_id: 1,
130+
verifying_contract: Address:: from([0x11u8; 20]),
131+
};
132+
133+
let test_value = 100u128;
134+
let test_address = Address::from([0x11u8; 20]);
135+
let message = msg::Receipt::new(test_address, test_value).unwrap();
136+
137+
// Create a new Ethereum signer
138+
let signer = PrivateKeySigner::random();
139+
140+
// Create signed message using the original signature
141+
let signed_message =
142+
Eip712SignedMessage::new(&domain_separator, message.clone(), &signer).unwrap();
143+
144+
// Get the original signature components
145+
let r = signed_message.signature.r();
146+
let s = signed_message.signature.s();
147+
let v = signed_message.signature.v();
148+
149+
// Create a malleated signature by changing the s value
150+
// Get the Secp256k1 curve order
151+
let n = U256::from_str_radix(
152+
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
153+
16,
154+
)
155+
.unwrap();
156+
let s_malleated = n - s;
157+
let v_malleated = !v; // Flip the parity bit
158+
159+
// Create a new signature with the malleated components
160+
let signature_malleated = Signature::new(r, s_malleated, v_malleated);
161+
162+
// Create a new signed message with the malleated signature
163+
let signed_message_malleated = Eip712SignedMessage {
164+
message: message.clone(),
165+
signature: signature_malleated,
166+
};
167+
168+
// Verify both signatures recover to the same signer
169+
let signer_address = signer.address();
170+
let recovered_address = signed_message.recover_signer(&domain_separator).unwrap();
171+
let recovered_address_malleated = signed_message_malleated
172+
.recover_signer(&domain_separator)
173+
.unwrap();
174+
175+
assert_eq!(
176+
recovered_address, signer_address,
177+
"Original signature should recover to the correct address"
178+
);
179+
assert_eq!(
180+
recovered_address_malleated, signer_address,
181+
"Malleated signature should recover to the same address"
182+
);
183+
184+
// Verify that the raw signatures are different
185+
assert_ne!(
186+
signed_message.signature.as_bytes(),
187+
signed_message_malleated.signature.as_bytes(),
188+
"Raw signature bytes should be different"
189+
);
190+
191+
// WITHOUT our fix, these would generate different IDs:
192+
let raw_sig_id_1 = signed_message.signature.get_signature_bytes();
193+
let raw_sig_id_2 = signed_message_malleated.signature.get_signature_bytes();
194+
assert_ne!(
195+
raw_sig_id_1, raw_sig_id_2,
196+
"Using only raw signatures would fail to detect duplicates"
197+
);
198+
199+
// WITH our fix, these should generate the same unique ID:
200+
let unique_id_1 = signed_message.unique_hash(&domain_separator).unwrap();
201+
let unique_id_2 = signed_message_malleated
202+
.unique_hash(&domain_separator)
203+
.unwrap();
204+
205+
assert_eq!(
206+
unique_id_1, unique_id_2,
207+
"Our fix should generate the same ID for both original and malleated signatures"
208+
);
209+
210+
// Bonus: Verify that different messages generate different IDs
211+
let different_message = msg::Receipt::new(test_address, test_value + 1).unwrap();
212+
let different_signed_message =
213+
Eip712SignedMessage::new(&domain_separator, different_message, &signer).unwrap();
214+
let different_unique_id = different_signed_message
215+
.unique_hash(&domain_separator)
216+
.unwrap();
217+
218+
assert_ne!(
219+
unique_id_1, different_unique_id,
220+
"Different messages should generate different unique IDs"
221+
);
222+
}
223+
}

0 commit comments

Comments
 (0)