verity_verify_local/
ecdsa.rs1use easy_hasher::easy_hasher;
2
3pub fn validate_ecdsa_signature(
5 signature_hex: &String,
6 message: &String,
7 public_key_hex: &String,
8) -> anyhow::Result<bool> {
9 let signature_hex = signature_hex.replace("0x", "");
10 let public_key_hex = public_key_hex.replace("0x", "");
11
12 let recovered_key = recover_address_from_eth_signature(signature_hex, message.clone())?;
13 let is_equal = recovered_key.to_lowercase() == public_key_hex.to_lowercase();
14
15 Ok(is_equal)
16}
17
18fn recover_address_from_eth_signature(
20 metamask_signature: String,
21 message: String,
22) -> anyhow::Result<String> {
23 let metamask_signature = hex::decode(metamask_signature)?;
24
25 let signature_bytes: [u8; 64] = metamask_signature[0..64].try_into()?;
26 let signature_bytes_64 = libsecp256k1::Signature::parse_standard(&signature_bytes)?;
27
28 let recovery_id = metamask_signature[64];
29 let recovery_id_byte = libsecp256k1::RecoveryId::parse_rpc(recovery_id)?;
30
31 let message_bytes: [u8; 32] = hash_eth_message(message).try_into().unwrap();
32 let message_bytes_32 = libsecp256k1::Message::parse(&message_bytes);
33
34 let public_key =
35 libsecp256k1::recover(&message_bytes_32, &signature_bytes_64, &recovery_id_byte)?;
36
37 let address = get_address_from_public_key(public_key.serialize_compressed().to_vec()).unwrap();
38
39 Ok(address)
40}
41
42fn hash_eth_message<T: AsRef<[u8]>>(message: T) -> Vec<u8> {
44 const PREFIX: &str = "\x19Ethereum Signed Message:\n";
45
46 let message = message.as_ref();
47 let len = message.len();
48 let len_string = len.to_string();
49
50 let mut eth_message = Vec::with_capacity(PREFIX.len() + len_string.len() + len);
51 eth_message.extend_from_slice(PREFIX.as_bytes());
52 eth_message.extend_from_slice(len_string.as_bytes());
53 eth_message.extend_from_slice(message);
54
55 easy_hasher::raw_keccak256(eth_message).to_vec()
56}
57
58fn get_address_from_public_key(public_key: Vec<u8>) -> Result<String, String> {
60 if public_key.len() != 33 {
61 return Err("INVALID_PK_LENGTH".to_string());
62 }
63
64 let pub_key_arr: [u8; 33] = public_key[..].try_into().unwrap();
65 let pub_key = libsecp256k1::PublicKey::parse_compressed(&pub_key_arr)
66 .map_err(|e| format!("{}", e))?
67 .serialize();
68
69 let keccak256 = easy_hasher::raw_keccak256(pub_key[1..].to_vec());
70 let keccak256_hex = keccak256.to_hex_string();
71 let address: String = keccak256_hex[24..].to_string();
72
73 Ok(address)
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_validate_signature() {
82 let message =
83 "77ad25504555a257256919c8372236844ef886c4a1b2efa157be0e1e3a26d40a".to_string();
84 let public_key = "c4bb0da5d7cc269bca64a55e2149e6dc91dc7157".to_string();
85 let expected_signature =
86 "eeae5aee33e7ae31c84ff37dd85e1e25d8750a2b8598c67795b6246e18cb8ffe1b45b9e394b57e0b840e6d8e8b501c75a44b4580904660f11c8a435bbb8a37411c".to_string();
87
88 let is_valid =
89 validate_ecdsa_signature(&expected_signature, &message, &public_key).unwrap();
90
91 assert!(is_valid, "invalid message or signature")
92 }
93}