feat: add file output for attestation collateral and quote data

- Write attestation collateral and quote details to various files.
This commit is contained in:
Harald Hoyer 2025-03-06 15:28:52 +01:00
parent 63b9d6f6ee
commit 71de3a53bc
Signed by: harald
GPG key ID: F519A1143B3FBE32
6 changed files with 159 additions and 11 deletions

1
Cargo.lock generated
View file

@ -6455,6 +6455,7 @@ dependencies = [
"tracing",
"tracing-subscriber",
"url",
"x509-cert",
"zksync_basic_types",
"zksync_types",
"zksync_web3_decl",

View file

@ -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"

View file

@ -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

View file

@ -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.

View file

@ -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<bool> {
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?;

View file

@ -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<bool> {
pub fn verify(&mut self) -> Result<bool> {
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<bool> {
use std::fs;
let quote_verification_result = verify_attestation_quote(attestation_quote_bytes)?;
log_quote_verification_summary(&quote_verification_result);
@ -116,12 +237,29 @@ pub async fn verify_batch_proof(
if !is_quote_matching_policy(attestation_policy, &quote_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<QuoteVerificationResult> {