From 71de3a53bcba679ff64c2feb5399a38c33aec4b2 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 6 Mar 2025 15:28:52 +0100 Subject: [PATCH] feat: add file output for attestation collateral and quote data - Write attestation collateral and quote details to various files. --- Cargo.lock | 1 + Cargo.toml | 2 +- bin/verify-era-proof-attestation/Cargo.toml | 1 + bin/verify-era-proof-attestation/src/args.rs | 11 +- bin/verify-era-proof-attestation/src/main.rs | 3 + .../src/verification.rs | 152 +++++++++++++++++- 6 files changed, 159 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fdb34a6..649cd49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6455,6 +6455,7 @@ dependencies = [ "tracing", "tracing-subscriber", "url", + "x509-cert", "zksync_basic_types", "zksync_types", "zksync_web3_decl", diff --git a/Cargo.toml b/Cargo.toml index 33f2b2e..e3c50a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,7 +69,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "ansi" tracing-test = { version = "0.2.5", features = ["no-env-filter"] } url = "2.5.2" webpki-roots = "0.26.1" -x509-cert = { version = "0.2", features = ["builder", "signature"] } +x509-cert = { version = "0.2", features = ["builder", "signature", "pem"] } zeroize = { version = "1.7.0", features = ["serde"] } zksync_basic_types = "=0.1.0" zksync_types = "=0.1.0" diff --git a/bin/verify-era-proof-attestation/Cargo.toml b/bin/verify-era-proof-attestation/Cargo.toml index 70e6379..f7be868 100644 --- a/bin/verify-era-proof-attestation/Cargo.toml +++ b/bin/verify-era-proof-attestation/Cargo.toml @@ -21,6 +21,7 @@ tokio.workspace = true tracing.workspace = true tracing-subscriber.workspace = true url.workspace = true +x509-cert.workspace = true zksync_basic_types.workspace = true zksync_types.workspace = true zksync_web3_decl.workspace = true diff --git a/bin/verify-era-proof-attestation/src/args.rs b/bin/verify-era-proof-attestation/src/args.rs index cea2a1c..fb94ce5 100644 --- a/bin/verify-era-proof-attestation/src/args.rs +++ b/bin/verify-era-proof-attestation/src/args.rs @@ -1,11 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023-2024 Matter Labs +// Copyright (c) 2023-2025 Matter Labs use anyhow::{anyhow, Result}; use clap::{ArgGroup, Args, Parser}; use std::time::Duration; -use teepot::log::LogLevelParser; -use teepot::sgx::{parse_tcb_levels, EnumSet, TcbLevel}; +use teepot::{ + log::LogLevelParser, + sgx::{parse_tcb_levels, EnumSet, TcbLevel}, +}; use tracing_subscriber::filter::LevelFilter; use url::Url; use zksync_basic_types::L1BatchNumber; @@ -43,6 +45,9 @@ pub struct Arguments { /// Criteria for valid attestation policy. Invalid proofs will be rejected. #[clap(flatten)] pub attestation_policy: AttestationPolicyArgs, + /// Save artifacts needed for verification + #[clap(short = 's', long = "save")] + pub save: bool, } /// Attestation policy implemented as a set of criteria that must be met by SGX attestation. diff --git a/bin/verify-era-proof-attestation/src/main.rs b/bin/verify-era-proof-attestation/src/main.rs index 15f4f3b..98fbd54 100644 --- a/bin/verify-era-proof-attestation/src/main.rs +++ b/bin/verify-era-proof-attestation/src/main.rs @@ -91,6 +91,7 @@ async fn verify_batches_proofs( &http_client, &node_client, &args.attestation_policy, + args.save, ) .await?; @@ -141,6 +142,7 @@ async fn verify_batch_proofs( http_client: &Client, node_client: &MainNodeClient, attestation_policy: &AttestationPolicyArgs, + save: bool, ) -> Result { let proofs = get_proofs(stop_receiver, batch_number, http_client, rpc_url).await?; let batch_no = batch_number.0; @@ -178,6 +180,7 @@ async fn verify_batch_proofs( node_client, &proof.signature.unwrap_or_default(), L1BatchNumber(proof.l1_batch_number), + save, ) .await?; diff --git a/bin/verify-era-proof-attestation/src/verification.rs b/bin/verify-era-proof-attestation/src/verification.rs index bfe16ea..445b1b2 100644 --- a/bin/verify-era-proof-attestation/src/verification.rs +++ b/bin/verify-era-proof-attestation/src/verification.rs @@ -8,13 +8,16 @@ use secp256k1::{ ecdsa::{RecoverableSignature, RecoveryId, Signature}, Message, SECP256K1, }; +use std::fs; use teepot::{ client::TcbLevel, ethereum::{public_key_to_ethereum_address, recover_signer}, prover::reportdata::ReportData, quote::{tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult, Report}, + sgx::Collateral, }; use tracing::{debug, info, trace, warn}; +use x509_cert::der::Encode; use zksync_basic_types::{L1BatchNumber, H256}; struct TeeProof { @@ -32,7 +35,7 @@ impl TeeProof { } } - pub fn verify(&self) -> Result { + pub fn verify(&mut self) -> Result { match &self.report { ReportData::V0(report) => { debug!("ReportData::V0"); @@ -75,6 +78,9 @@ impl TeeProof { ); if ethereum_address_from_signature == ethereum_address_from_report { info!("Had to use RecoveryId::{rec_id:?}"); + self.signature.push( + u8::try_from(i32::from(rec_id)).context("recovery id to u8")? + 27, + ); return Ok(true); } } @@ -100,15 +106,130 @@ impl TeeProof { } } +fn save_verification_artifacts( + batch_number: L1BatchNumber, + signature: &[u8], + root_hash: &H256, + attestation_quote_bytes: &[u8], + collateral: &Collateral, +) -> Result<()> { + fs::write( + format!("{}_signature.hex", batch_number), + hex::encode(signature), + )?; + fs::write( + format!("{}_root_hash.hex", batch_number), + format!("{:x}", root_hash), + )?; + fs::write( + format!("{}_quote.bin", batch_number), + attestation_quote_bytes, + )?; + fs::write( + format!("{}_quote.bin.hex", batch_number), + hex::encode(attestation_quote_bytes), + )?; + + let certs = x509_cert::certificate::CertificateInner::< + x509_cert::certificate::Rfc5280 + >::load_pem_chain(collateral.pck_crl_issuer_chain.split_last().unwrap().1)?; + + let cert = certs + .into_iter() + .find(|cert| { + cert.tbs_certificate + .subject + .to_string() + .contains("PCK Platform CA") + }) + .ok_or(anyhow!("PCK Platform CA cert not found"))?; + + fs::write( + format!("{}_platformDer.hex", batch_number), + hex::encode(&cert.to_der()?), + ) + .context("Failed to write PCK Platform CA to platformDer.hex")?; + + debug!( + "Platform CA serial {}", + cert.tbs_certificate.serial_number.to_string() + ); + + fs::write( + format!("{}_rootCrlDer.hex", batch_number), + collateral.root_ca_crl.split_last().unwrap().1, + ) + .context("Failed to write root_ca_crl to rootCrlDer.hex")?; + + fs::write( + format!("{}_platformCrlDer.hex", batch_number), + hex::encode(collateral.pck_crl.split_last().unwrap().1), + ) + .context("Failed to write pck_crl to platformCrlDer.hex")?; + + let certs = x509_cert::certificate::CertificateInner::< + x509_cert::certificate::Rfc5280 + >::load_pem_chain(collateral.tcb_info_issuer_chain.split_last().unwrap().1)?; + + let cert = certs + .into_iter() + .find(|cert| { + cert.tbs_certificate + .subject + .to_string() + .contains("TCB Signing") + }) + .ok_or(anyhow!("TCB Signing cert not found"))?; + + debug!("TCB cert {}", cert.tbs_certificate.subject.to_string()); + debug!( + "TCB serial {}", + cert.tbs_certificate.serial_number.to_string() + ); + + fs::write( + format!("{}_tcbDer.hex", batch_number), + hex::encode(&cert.to_der()?), + ) + .context("Failed to write TCB Signing CA to tcbDer.hex")?; + + fs::write( + format!("{}_tcb_info.json", batch_number), + collateral.tcb_info.split_last().unwrap().1, + ) + .context("Failed to write tcb_info to tcb_info.json")?; + + let certs = x509_cert::certificate::CertificateInner::< + x509_cert::certificate::Rfc5280 + >::load_pem_chain(collateral.qe_identity_issuer_chain.split_last().unwrap().1)?; + + let _ = certs + .into_iter() + .find(|qe_cert| { + qe_cert + .tbs_certificate + .serial_number + .eq(&cert.tbs_certificate.serial_number) + }) + .ok_or(anyhow!("QE identity cert != TCB cert"))?; + + fs::write( + format!("{}_qe_identity.json", batch_number), + collateral.qe_identity.split_last().unwrap().1, + ) + .context("Failed to write qe_identity to qe_identity.json")?; + + Ok(()) +} + pub async fn verify_batch_proof( attestation_quote_bytes: &[u8], attestation_policy: &AttestationPolicyArgs, node_client: &impl JsonRpcClient, signature: &[u8], batch_number: L1BatchNumber, + save: bool, ) -> Result { - use std::fs; - let quote_verification_result = verify_attestation_quote(attestation_quote_bytes)?; log_quote_verification_summary("e_verification_result); @@ -116,12 +237,29 @@ pub async fn verify_batch_proof( if !is_quote_matching_policy(attestation_policy, "e_verification_result) { return Ok(false); } - + let QuoteVerificationResult { + quote, collateral, .. + } = quote_verification_result; let root_hash = node_client.get_root_hash(batch_number).await?; - let report_data_bytes = quote_verification_result.quote.get_report_data(); + let report_data_bytes = quote.get_report_data(); + let report_data = ReportData::try_from(report_data_bytes)?; - let tee_proof = TeeProof::new(report_data, root_hash, signature.to_vec()); - tee_proof.verify() + + let mut tee_proof = TeeProof::new(report_data, root_hash, signature.to_vec()); + + let res = tee_proof.verify(); + + if save { + save_verification_artifacts( + batch_number, + tee_proof.signature.as_slice(), + &root_hash, + attestation_quote_bytes, + &collateral, + )?; + } + + res } pub fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result {