mirror of
https://github.com/matter-labs/teepot.git
synced 2025-07-21 07:03:56 +02:00
Merge pull request #189 from matter-labs/patrick/sgx-rpc-attestation-verifier
feat(verify-attestation): RPC attestation and batch signature verification binary
This commit is contained in:
commit
6b0b0b69ca
11 changed files with 2991 additions and 170 deletions
1
.github/workflows/nix.yml
vendored
1
.github/workflows/nix.yml
vendored
|
@ -76,6 +76,7 @@ jobs:
|
|||
- { nixpackage: 'container-self-attestation-test-sgx-dcap' }
|
||||
- { nixpackage: 'container-self-attestation-test-sgx-azure' }
|
||||
- { nixpackage: 'container-verify-attestation-sgx' }
|
||||
- { nixpackage: 'container-verify-era-proof-attestation-sgx' }
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v27
|
||||
|
|
2902
Cargo.lock
generated
2902
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -42,6 +42,7 @@ pgp = "0.13"
|
|||
p256 = "0.13.2"
|
||||
pkcs8 = { version = "0.10" }
|
||||
rand = "0.8"
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
ring = { version = "0.17.8", features = ["std"], default-features = false }
|
||||
rsa = { version = "0.9.6", features = ["sha2", "pem"] }
|
||||
rustls = { version = "0.22" }
|
||||
|
@ -60,7 +61,10 @@ tracing = "0.1"
|
|||
tracing-actix-web = "0.7"
|
||||
tracing-log = "0.2"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
url = "2.5.2"
|
||||
x509-cert = { version = "0.2", features = ["builder", "signature"] }
|
||||
zeroize = { version = "1.7.0", features = ["serde"] }
|
||||
webpki-roots = "0.26.1"
|
||||
zksync_basic_types = "=0.1.0"
|
||||
zksync_web3_decl = "=0.1.0"
|
||||
zksync_types = "=0.1.0"
|
||||
|
|
21
bin/verify-era-proof-attestation/Cargo.toml
Normal file
21
bin/verify-era-proof-attestation/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "verify-era-proof-attestation"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
clap.workspace = true
|
||||
hex.workspace = true
|
||||
reqwest.workspace = true
|
||||
secp256k1.workspace = true
|
||||
serde.workspace = true
|
||||
teepot.workspace = true
|
||||
tokio.workspace = true
|
||||
url.workspace = true
|
||||
zksync_basic_types.workspace = true
|
||||
zksync_types.workspace = true
|
||||
zksync_web3_decl.workspace = true
|
176
bin/verify-era-proof-attestation/src/main.rs
Normal file
176
bin/verify-era-proof-attestation/src/main.rs
Normal file
|
@ -0,0 +1,176 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
|
||||
//! Tool for SGX attestation and batch signature verification
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use clap::Parser;
|
||||
use reqwest::Client;
|
||||
use secp256k1::{constants::PUBLIC_KEY_SIZE, ecdsa::Signature, Message, PublicKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use teepot::{
|
||||
client::TcbLevel,
|
||||
sgx::{tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult},
|
||||
};
|
||||
use url::Url;
|
||||
use zksync_basic_types::{L1BatchNumber, H256};
|
||||
use zksync_types::L2ChainId;
|
||||
use zksync_web3_decl::{
|
||||
client::{Client as NodeClient, L2},
|
||||
error::ClientRpcContext,
|
||||
namespaces::ZksNamespaceClient,
|
||||
};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author = "Matter Labs", version, about = "SGX attestation and batch signature verifier", long_about = None)]
|
||||
struct Arguments {
|
||||
/// The batch number for which we want to verify the attestation and signature.
|
||||
#[clap(short = 'n', long)]
|
||||
batch_number: L1BatchNumber,
|
||||
/// URL of the RPC server to query for the batch attestation and signature.
|
||||
#[clap(short, long)]
|
||||
rpc_url: Url,
|
||||
/// Chain ID of the network to query.
|
||||
#[clap(short, long, default_value_t = L2ChainId::default().as_u64())]
|
||||
chain_id: u64,
|
||||
}
|
||||
|
||||
trait JsonRpcClient {
|
||||
async fn get_root_hash(&self, batch_number: L1BatchNumber) -> Result<H256>;
|
||||
// TODO implement get_tee_proofs(batch_number, tee_type) once zksync_web3_decl crate is updated
|
||||
}
|
||||
|
||||
struct MainNodeClient(NodeClient<L2>);
|
||||
|
||||
impl JsonRpcClient for MainNodeClient {
|
||||
async fn get_root_hash(&self, batch_number: L1BatchNumber) -> Result<H256> {
|
||||
self.0
|
||||
.get_l1_batch_details(batch_number)
|
||||
.rpc_context("get_l1_batch_details")
|
||||
.await?
|
||||
.and_then(|res| res.base.root_hash)
|
||||
.ok_or_else(|| anyhow!("No root hash found for batch #{}", batch_number))
|
||||
}
|
||||
}
|
||||
|
||||
// JSON-RPC request and response structures for fetching TEE proofs
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct GetProofsRequest {
|
||||
jsonrpc: String,
|
||||
id: u32,
|
||||
method: String,
|
||||
params: (L1BatchNumber, String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct GetProofsResponse {
|
||||
jsonrpc: String,
|
||||
result: Vec<Proof>,
|
||||
id: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Proof {
|
||||
#[serde(rename = "l1BatchNumber")]
|
||||
l1_batch_number: u32,
|
||||
#[serde(rename = "teeType")]
|
||||
tee_type: String,
|
||||
pubkey: Vec<u8>,
|
||||
signature: Vec<u8>,
|
||||
proof: Vec<u8>,
|
||||
#[serde(rename = "provedAt")]
|
||||
proved_at: String,
|
||||
attestation: Vec<u8>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let args = Arguments::parse();
|
||||
let node_client: NodeClient<L2> = NodeClient::http(args.rpc_url.clone().into())
|
||||
.context("failed creating JSON-RPC client for main node")?
|
||||
.for_network(
|
||||
L2ChainId::try_from(args.chain_id)
|
||||
.map_err(anyhow::Error::msg)?
|
||||
.into(),
|
||||
)
|
||||
.build();
|
||||
let node_client = MainNodeClient(node_client);
|
||||
let http_client = Client::new();
|
||||
let request = GetProofsRequest {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
id: 1,
|
||||
method: "unstable_getTeeProofs".to_string(),
|
||||
params: (args.batch_number, "Sgx".to_string()),
|
||||
};
|
||||
let response = http_client
|
||||
.post(args.rpc_url)
|
||||
.json(&request)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.json::<GetProofsResponse>()
|
||||
.await?;
|
||||
|
||||
for proof in response.result {
|
||||
println!("Verifying batch #{}", proof.l1_batch_number);
|
||||
let quote_verification_result = verify_attestation_quote(&proof.attestation)?;
|
||||
print_quote_verification_summary("e_verification_result);
|
||||
let public_key = PublicKey::from_slice(
|
||||
"e_verification_result.quote.report_body.reportdata[..PUBLIC_KEY_SIZE],
|
||||
)?;
|
||||
println!("Public key from attestation quote: {}", public_key);
|
||||
let root_hash = node_client.get_root_hash(args.batch_number).await?;
|
||||
println!("Root hash: {}", root_hash);
|
||||
verify_signature(&proof.signature, public_key, root_hash)?;
|
||||
println!();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_signature(signature: &[u8], public_key: PublicKey, root_hash: H256) -> Result<()> {
|
||||
let signature = Signature::from_compact(signature)?;
|
||||
let root_hash_msg = Message::from_digest_slice(&root_hash.0)?;
|
||||
if signature.verify(&root_hash_msg, &public_key).is_ok() {
|
||||
println!("Signature verified successfully");
|
||||
} else {
|
||||
println!("Failed to verify signature");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result<QuoteVerificationResult> {
|
||||
println!(
|
||||
"Verifying quote ({} bytes)...",
|
||||
attestation_quote_bytes.len()
|
||||
);
|
||||
let collateral =
|
||||
tee_qv_get_collateral(attestation_quote_bytes).context("Failed to get collateral")?;
|
||||
let unix_time: i64 = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)?
|
||||
.as_secs() as _;
|
||||
verify_quote_with_collateral(attestation_quote_bytes, Some(&collateral), unix_time)
|
||||
.context("Failed to verify quote with collateral")
|
||||
}
|
||||
|
||||
fn print_quote_verification_summary(quote_verification_result: &QuoteVerificationResult) {
|
||||
let QuoteVerificationResult {
|
||||
collateral_expired,
|
||||
result,
|
||||
quote,
|
||||
advisories,
|
||||
..
|
||||
} = quote_verification_result;
|
||||
if *collateral_expired {
|
||||
println!("Freshly fetched collateral expired");
|
||||
}
|
||||
let tcblevel = TcbLevel::from(*result);
|
||||
for advisory in advisories {
|
||||
println!("\tInfo: Advisory ID: {advisory}");
|
||||
}
|
||||
println!("Quote verification result: {}", tcblevel);
|
||||
println!("mrsigner: {}", hex::encode(quote.report_body.mrsigner));
|
||||
println!("mrenclave: {}", hex::encode(quote.report_body.mrenclave));
|
||||
println!("reportdata: {}", hex::encode(quote.report_body.reportdata));
|
||||
}
|
|
@ -54,6 +54,9 @@
|
|||
shells = {
|
||||
default = "teepot";
|
||||
};
|
||||
devShells = {
|
||||
default = "teepot";
|
||||
};
|
||||
};
|
||||
|
||||
outputs-builder = channels: {
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
{ teepotCrate }: teepotCrate.craneLib.cargoClippy (
|
||||
teepotCrate.commonArgs // {
|
||||
pname = "teepot";
|
||||
inherit (teepotCrate) cargoArtifacts NIX_OUTPATH_USED_AS_RANDOM_SEED;
|
||||
inherit (teepotCrate) cargoArtifacts;
|
||||
}
|
||||
)
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2024 Matter Labs
|
||||
{ dockerTools
|
||||
, buildEnv
|
||||
, teepot
|
||||
, openssl
|
||||
, curl
|
||||
, nixsgx
|
||||
, pkg-config
|
||||
}:
|
||||
dockerTools.buildLayeredImage {
|
||||
name = "verify-era-proof-attestation";
|
||||
|
||||
config.Entrypoint = [ "${teepot.teepot.verify_era_proof_attestation}/bin/verify-era-proof-attestation" ];
|
||||
config.Env = [ "LD_LIBRARY_PATH=/lib" ];
|
||||
contents = buildEnv {
|
||||
name = "image-root";
|
||||
|
||||
paths = with dockerTools; with nixsgx;[
|
||||
pkg-config
|
||||
openssl.out
|
||||
curl.out
|
||||
sgx-dcap.quote_verify
|
||||
sgx-dcap.default_qpl
|
||||
teepot.teepot.verify_era_proof_attestation
|
||||
usrBinEnv
|
||||
binSh
|
||||
caCertificates
|
||||
fakeNss
|
||||
];
|
||||
pathsToLink = [ "/bin" "/lib" "/etc" "/share" ];
|
||||
};
|
||||
}
|
|
@ -3,9 +3,7 @@
|
|||
{ teepotCrate }: teepotCrate.craneLib.buildPackage (
|
||||
teepotCrate.commonArgs // {
|
||||
pname = "teepot";
|
||||
inherit (teepotCrate) cargoArtifacts
|
||||
NIX_OUTPATH_USED_AS_RANDOM_SEED;
|
||||
|
||||
inherit (teepotCrate) cargoArtifacts;
|
||||
|
||||
passthru = {
|
||||
inherit (teepotCrate) rustPlatform
|
||||
|
@ -13,7 +11,6 @@
|
|||
commonArgs
|
||||
craneLib
|
||||
cargoArtifacts;
|
||||
NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa";
|
||||
};
|
||||
|
||||
outputs = [
|
||||
|
@ -29,6 +26,7 @@
|
|||
"vault_admin"
|
||||
"vault_unseal"
|
||||
"verify_attestation"
|
||||
"verify_era_proof_attestation"
|
||||
];
|
||||
postInstall = ''
|
||||
removeReferencesToVendoredSources "$out" "$cargoVendorDir"
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
}:
|
||||
mkShell {
|
||||
inputsFrom = [ teepot.teepot ];
|
||||
|
||||
packages = [
|
||||
dive
|
||||
taplo
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
, rust-bin
|
||||
, pkgs
|
||||
, src
|
||||
, ...
|
||||
, openssl
|
||||
}:
|
||||
let
|
||||
rustVersion = rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
|
||||
|
@ -24,6 +24,7 @@ let
|
|||
];
|
||||
|
||||
buildInputs = [
|
||||
openssl
|
||||
nixsgx.sgx-sdk
|
||||
nixsgx.sgx-dcap
|
||||
nixsgx.sgx-dcap.quote_verify
|
||||
|
@ -31,7 +32,6 @@ let
|
|||
|
||||
strictDeps = true;
|
||||
|
||||
|
||||
src = with lib.fileset; toSource {
|
||||
root = src;
|
||||
fileset = unions [
|
||||
|
@ -46,12 +46,15 @@ let
|
|||
};
|
||||
|
||||
checkType = "debug";
|
||||
env = {
|
||||
OPENSSL_NO_VENDOR = "1";
|
||||
NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa";
|
||||
};
|
||||
};
|
||||
|
||||
cargoArtifacts = craneLib.buildDepsOnly (commonArgs // {
|
||||
pname = "teepot-workspace";
|
||||
inherit NIX_OUTPATH_USED_AS_RANDOM_SEED;
|
||||
});
|
||||
NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa";
|
||||
in
|
||||
{
|
||||
inherit rustPlatform
|
||||
|
@ -59,5 +62,4 @@ in
|
|||
commonArgs
|
||||
craneLib
|
||||
cargoArtifacts;
|
||||
NIX_OUTPATH_USED_AS_RANDOM_SEED = "aaaaaaaaaa";
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue