mirror of
https://github.com/matter-labs/teepot.git
synced 2025-07-21 23:23:57 +02:00
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:
commit
c48cbc636d
7 changed files with 879 additions and 179 deletions
900
Cargo.lock
generated
900
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -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"
|
||||||
|
|
|
@ -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...
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
ArgSource::File(path) => fs::read(&path)?,
|
||||||
|
ArgSource::Stdin => {
|
||||||
|
let mut quote = Vec::new();
|
||||||
std::io::stdin()
|
std::io::stdin()
|
||||||
.read_to_end(&mut myquote)
|
.read_to_end(&mut quote)
|
||||||
.context("Failed to read quote from stdin")?;
|
.context("Failed to read attestation quote from stdin")?;
|
||||||
|
quote
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let quote_verification_result = verify_attestation_quote(&attestation_quote_bytes)?;
|
||||||
|
print_quote_verification_summary("e_verification_result);
|
||||||
|
match &args.command {
|
||||||
|
Some(SubCommands::SignVerify(signature_args)) => {
|
||||||
|
verify_signature("e_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 = "e_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(())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue