44use crate :: { args:: AttestationPolicyArgs , client:: JsonRpcClient } ;
55use anyhow:: { Context , Result } ;
66use hex:: encode;
7- use secp256k1:: { constants:: PUBLIC_KEY_SIZE , ecdsa:: Signature , Message , PublicKey } ;
7+ use secp256k1:: {
8+ ecdsa:: { RecoverableSignature , RecoveryId } ,
9+ Message , PublicKey , SECP256K1 ,
10+ } ;
11+ use sha3:: { Digest , Keccak256 } ;
812use teepot:: {
913 client:: TcbLevel ,
1014 quote:: {
@@ -27,22 +31,25 @@ pub async fn verify_batch_proof(
2731 }
2832
2933 let batch_no = batch_number. 0 ;
30-
31- let public_key = PublicKey :: from_slice (
32- & quote_verification_result. quote . get_report_data ( ) [ ..PUBLIC_KEY_SIZE ] ,
33- ) ?;
34- debug ! ( batch_no, "public key: {}" , public_key) ;
35-
3634 let root_hash = node_client. get_root_hash ( batch_number) . await ?;
37- debug ! ( batch_no, "root hash: {}" , root_hash) ;
35+ let ethereum_address_from_quote = & quote_verification_result. quote . get_report_data ( ) [ ..20 ] ;
36+ let signature_array: & [ u8 ; 65 ] = signature. try_into ( ) ?;
37+ let ethereum_address_from_signature = recover_signer ( signature_array, root_hash) ?;
38+ let verification_successful = & ethereum_address_from_signature == ethereum_address_from_quote;
39+ debug ! (
40+ batch_no,
41+ "Root hash: {}. Ethereum address from the attestation quote: {}. Ethereum address from the signature: {}." ,
42+ root_hash,
43+ encode( ethereum_address_from_quote) ,
44+ encode( ethereum_address_from_signature) ,
45+ ) ;
3846
39- let is_verified = verify_signature ( signature, public_key, root_hash) ?;
40- if is_verified {
47+ if verification_successful {
4148 info ! ( batch_no, signature = %encode( signature) , "Signature verified successfully." ) ;
4249 } else {
4350 warn ! ( batch_no, signature = %encode( signature) , "Failed to verify signature!" ) ;
4451 }
45- Ok ( is_verified )
52+ Ok ( verification_successful )
4653}
4754
4855pub fn verify_attestation_quote ( attestation_quote_bytes : & [ u8 ] ) -> Result < QuoteVerificationResult > {
@@ -85,12 +92,6 @@ pub fn log_quote_verification_summary(quote_verification_result: &QuoteVerificat
8592 ) ;
8693}
8794
88- fn verify_signature ( signature : & [ u8 ] , public_key : PublicKey , root_hash : H256 ) -> Result < bool > {
89- let signature = Signature :: from_compact ( signature) ?;
90- let root_hash_msg = Message :: from_digest_slice ( & root_hash. 0 ) ?;
91- Ok ( signature. verify ( & root_hash_msg, & public_key) . is_ok ( ) )
92- }
93-
9495fn is_quote_matching_policy (
9596 attestation_policy : & AttestationPolicyArgs ,
9697 quote_verification_result : & QuoteVerificationResult ,
@@ -136,3 +137,29 @@ fn check_policy(policy: Option<&str>, actual_value: &[u8], field_name: &str) ->
136137 }
137138 true
138139}
140+
141+ /// Equivalent to the ecrecover precompile, ensuring that the signatures we produce off-chain
142+ /// can be recovered on-chain.
143+ pub fn recover_signer ( sig : & [ u8 ; 65 ] , root_hash : H256 ) -> Result < [ u8 ; 20 ] > {
144+ let root_hash_bytes = root_hash. as_bytes ( ) ;
145+ let msg = Message :: from_digest_slice ( root_hash_bytes) ?;
146+ let sig = RecoverableSignature :: from_compact (
147+ & sig[ 0 ..64 ] ,
148+ RecoveryId :: from_i32 ( sig[ 64 ] as i32 - 27 ) ?,
149+ ) ?;
150+ let public = SECP256K1 . recover_ecdsa ( & msg, & sig) ?;
151+ Ok ( public_key_to_ethereum_address ( & public) )
152+ }
153+
154+ /// Converts a public key into an Ethereum address by hashing the encoded public key with Keccak256.
155+ fn public_key_to_ethereum_address ( public : & PublicKey ) -> [ u8 ; 20 ] {
156+ let public_key_bytes = public. serialize_uncompressed ( ) ;
157+
158+ // Skip the first byte (0x04) which indicates uncompressed key
159+ let hash: [ u8 ; 32 ] = Keccak256 :: digest ( & public_key_bytes[ 1 ..] ) . into ( ) ;
160+
161+ // Take the last 20 bytes of the hash to get the Ethereum address
162+ let mut address = [ 0u8 ; 20 ] ;
163+ address. copy_from_slice ( & hash[ 12 ..] ) ;
164+ address
165+ }
0 commit comments