Merge pull request #156 from matter-labs/patrick/sgx-attestation-verifier

feat(verify-attestation): attestation and batch signature verification binary
This commit is contained in:
Harald Hoyer 2024-07-12 08:54:36 +02:00 committed by GitHub
commit c48cbc636d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 879 additions and 179 deletions

900
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -19,6 +19,7 @@ homepage = "https://github.com/matter-labs/teepot"
actix-http = "3" actix-http = "3"
actix-tls = "3" actix-tls = "3"
actix-web = { version = "4.5", features = ["rustls-0_22"] } actix-web = { version = "4.5", features = ["rustls-0_22"] }
alloy-primitives = "0.7.7"
anyhow = "1.0.82" anyhow = "1.0.82"
awc = { version = "3.4", features = ["rustls-0_22-webpki-roots"] } awc = { version = "3.4", features = ["rustls-0_22-webpki-roots"] }
base64 = "0.22.0" base64 = "0.22.0"
@ -34,7 +35,7 @@ getrandom = "0.2.14"
hex = { version = "0.4.3", features = ["std"], default-features = false } hex = { version = "0.4.3", features = ["std"], default-features = false }
intel-tee-quote-verification-rs = { package = "teepot-tee-quote-verification-rs", path = "crates/teepot-tee-quote-verification-rs", version = "0.2.3-alpha.1" } intel-tee-quote-verification-rs = { package = "teepot-tee-quote-verification-rs", path = "crates/teepot-tee-quote-verification-rs", version = "0.2.3-alpha.1" }
intel-tee-quote-verification-sys = { version = "0.2.1" } intel-tee-quote-verification-sys = { version = "0.2.1" }
secp256k1 = { version = "0.29", features = ["rand-std"] } secp256k1 = { version = "0.29", features = ["rand-std", "global-context"] }
log = "0.4" log = "0.4"
num-integer = "0.1.46" num-integer = "0.1.46"
num-traits = "0.2.18" num-traits = "0.2.18"

View file

@ -14,7 +14,7 @@ $ nix build -L .#container-self-attestation-test-sgx-azure && docker load -i res
docker run -i --init --rm --privileged --device /dev/sgx_enclave \ docker run -i --init --rm --privileged --device /dev/sgx_enclave \
matterlabsrobot/teepot-self-attestation-test-sgx-azure:latest \ matterlabsrobot/teepot-self-attestation-test-sgx-azure:latest \
| base64 -d --ignore-garbage \ | base64 -d --ignore-garbage \
| docker run -i --rm matterlabsrobot/verify-attestation-sgx:latest | docker run -i --rm matterlabsrobot/verify-attestation-sgx:latest -
aesm_service: warning: Turn to daemon. Use "--no-daemon" option to execute in foreground. aesm_service: warning: Turn to daemon. Use "--no-daemon" option to execute in foreground.
Gramine is starting. Parsing TOML manifest file, this may take some time... Gramine is starting. Parsing TOML manifest file, this may take some time...
@ -33,7 +33,7 @@ reportdata: 00000000000000000000000000000000000000000000000000000000000000000000
docker run -i --init --rm --privileged --device /dev/sgx_enclave \ docker run -i --init --rm --privileged --device /dev/sgx_enclave \
matterlabsrobot/teepot-self-attestation-test-sgx-dcap:latest \ matterlabsrobot/teepot-self-attestation-test-sgx-dcap:latest \
| base64 -d --ignore-garbage \ | base64 -d --ignore-garbage \
| docker run -i --rm matterlabsrobot/verify-attestation-sgx:latest | docker run -i --rm matterlabsrobot/verify-attestation-sgx:latest -
aesm_service: warning: Turn to daemon. Use "--no-daemon" option to execute in foreground. aesm_service: warning: Turn to daemon. Use "--no-daemon" option to execute in foreground.
Gramine is starting. Parsing TOML manifest file, this may take some time... Gramine is starting. Parsing TOML manifest file, this may take some time...
@ -50,7 +50,7 @@ On an outdated machine, this might look like this:
docker run -i --init --rm --privileged --device /dev/sgx_enclave \ docker run -i --init --rm --privileged --device /dev/sgx_enclave \
matterlabsrobot/teepot-self-attestation-test-sgx-dcap:latest \ matterlabsrobot/teepot-self-attestation-test-sgx-dcap:latest \
| base64 -d --ignore-garbage \ | base64 -d --ignore-garbage \
| docker run -i --rm matterlabsrobot/verify-attestation-sgx:latest | docker run -i --rm matterlabsrobot/verify-attestation-sgx:latest -
aesm_service: warning: Turn to daemon. Use "--no-daemon" option to execute in foreground. aesm_service: warning: Turn to daemon. Use "--no-daemon" option to execute in foreground.
Gramine is starting. Parsing TOML manifest file, this may take some time... Gramine is starting. Parsing TOML manifest file, this may take some time...

View file

@ -7,6 +7,9 @@ license.workspace = true
repository.workspace = true repository.workspace = true
[dependencies] [dependencies]
alloy-primitives.workspace = true
anyhow.workspace = true anyhow.workspace = true
clap.workspace = true
hex.workspace = true hex.workspace = true
secp256k1.workspace = true
teepot.workspace = true teepot.workspace = true

View file

@ -1,60 +1,134 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2023-2024 Matter Labs // Copyright (c) 2023-2024 Matter Labs
//! Simple TEE attestation verification test //! Tool for SGX attestation and batch signature verification
#![deny(missing_docs)] use alloy_primitives::B256;
#![deny(clippy::all)] use anyhow::{Context, Result};
use clap::{Args, Parser, Subcommand};
use secp256k1::{ecdsa::Signature, Message, PublicKey};
use std::{fs, io::Read, path::PathBuf, str::FromStr, time::UNIX_EPOCH};
use teepot::{
client::TcbLevel,
sgx::{tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult},
};
use anyhow::{bail, Context, Result}; #[derive(Parser, Debug)]
use std::io::Read; #[command(author = "Matter Labs", version, about = "SGX attestation and batch signature verifier", long_about = None)]
use std::time::UNIX_EPOCH; struct Arguments {
use teepot::client::TcbLevel; /// Attestation quote proving the signature originated from a TEE enclave.
use teepot::sgx::{tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult}; #[clap(name = "attestation_file", value_parser)]
attestation: ArgSource,
/// An optional subcommand, for instance, for optional signature verification.
#[clap(subcommand)]
command: Option<SubCommands>,
}
#[derive(Debug, Clone)]
enum ArgSource {
File(PathBuf),
Stdin,
}
impl FromStr for ArgSource {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"-" => Ok(ArgSource::Stdin),
_ => Ok(ArgSource::File(PathBuf::from(s))),
}
}
}
#[derive(Args, Debug)]
struct SignatureArgs {
/// File containing a batch signature signed within a TEE enclave.
#[arg(long)]
signature_file: PathBuf,
/// Batch root hash for signature verification.
#[arg(long)]
root_hash: B256,
}
#[derive(Subcommand, Debug)]
enum SubCommands {
/// Verify a batch signature signed within a TEE enclave.
SignVerify(SignatureArgs),
}
fn main() -> Result<()> { fn main() -> Result<()> {
// read myquote from stdin let args = Arguments::parse();
let mut myquote = Vec::new(); let attestation_quote_bytes = match args.attestation {
std::io::stdin() ArgSource::File(path) => fs::read(&path)?,
.read_to_end(&mut myquote) ArgSource::Stdin => {
.context("Failed to read quote from stdin")?; let mut quote = Vec::new();
std::io::stdin()
.read_to_end(&mut quote)
.context("Failed to read attestation quote from stdin")?;
quote
}
};
let quote_verification_result = verify_attestation_quote(&attestation_quote_bytes)?;
print_quote_verification_summary(&quote_verification_result);
match &args.command {
Some(SubCommands::SignVerify(signature_args)) => {
verify_signature(&quote_verification_result, signature_args)?;
}
None => {}
}
Ok(())
}
println!("Verifying quote ({} bytes)...", myquote.len()); fn verify_signature(
quote_verification_result: &QuoteVerificationResult,
let collateral = tee_qv_get_collateral(&myquote).context("Failed to get collateral")?; signature_args: &SignatureArgs,
) -> Result<()> {
let reportdata = &quote_verification_result.quote.report_body.reportdata;
let public_key = PublicKey::from_slice(reportdata)?;
println!("Public key from attestation quote: {}", public_key);
let signature_bytes = fs::read(&signature_args.signature_file)?;
let signature = Signature::from_compact(&signature_bytes)?;
let root_hash_msg = Message::from_digest_slice(&signature_args.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() let unix_time: i64 = std::time::SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)?
.unwrap()
.as_secs() as _; .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 { let QuoteVerificationResult {
collateral_expired, collateral_expired,
result, result,
quote, quote,
advisories, advisories,
.. ..
} = verify_quote_with_collateral(&myquote, Some(&collateral), unix_time.saturating_add(60)) } = quote_verification_result;
.context("Failed to verify quote with collateral")?; if *collateral_expired {
println!("Freshly fetched collateral expired");
if collateral_expired {
bail!("Freshly fetched collateral expired");
} }
let tcblevel = TcbLevel::from(*result);
let tcblevel = TcbLevel::from(result);
if tcblevel != TcbLevel::Ok {
println!("Quote verification result: {}", tcblevel);
}
for advisory in advisories { for advisory in advisories {
println!("\tInfo: Advisory ID: {advisory}"); println!("\tInfo: Advisory ID: {advisory}");
} }
println!("Quote verification result: {}", tcblevel);
println!("Quote verified successfully: {}", tcblevel);
println!("mrsigner: {}", hex::encode(quote.report_body.mrsigner)); println!("mrsigner: {}", hex::encode(quote.report_body.mrsigner));
println!("mrenclave: {}", hex::encode(quote.report_body.mrenclave)); println!("mrenclave: {}", hex::encode(quote.report_body.mrenclave));
println!("reportdata: {}", hex::encode(quote.report_body.reportdata)); println!("reportdata: {}", hex::encode(quote.report_body.reportdata));
Ok(())
} }

View file

@ -416,7 +416,7 @@ impl<'a> Deref for SgxQlQveCollateralT<'a> {
/// SGX/TDX Quote, presented as u8 vector. /// SGX/TDX Quote, presented as u8 vector.
/// ///
/// # Return /// # Return
/// Result type of quote_collecteral. /// Result type of quote_collateral.
/// ///
/// - **quote_collateral**\ /// - **quote_collateral**\
/// This is the Quote Certification Collateral retrieved based on Quote. /// This is the Quote Certification Collateral retrieved based on Quote.

View file

@ -10,7 +10,7 @@
dockerTools.buildLayeredImage { dockerTools.buildLayeredImage {
name = "verify-attestation-sgx"; name = "verify-attestation-sgx";
config.Cmd = [ "${teepot.teepot.verify_attestation}/bin/verify-attestation" ]; config.Entrypoint = [ "${teepot.teepot.verify_attestation}/bin/verify-attestation" ];
config.Env = [ "LD_LIBRARY_PATH=/lib" ]; config.Env = [ "LD_LIBRARY_PATH=/lib" ];
contents = buildEnv { contents = buildEnv {
name = "image-root"; name = "image-root";