mirror of
https://github.com/matter-labs/teepot.git
synced 2025-07-21 23:23:57 +02:00
Merge pull request #300 from matter-labs/darwin
feat: compat code for non `x86_64-linux`
This commit is contained in:
commit
93c35dad38
44 changed files with 1543 additions and 531 deletions
38
.github/workflows/nix-non-x86.yml
vendored
Normal file
38
.github/workflows/nix-non-x86.yml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
name: nix-non-x86
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
tags: ["*"]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
macos-latest:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
- uses: cachix/install-nix-action@v31
|
||||||
|
with:
|
||||||
|
extra_nix_config: |
|
||||||
|
access-tokens = github.com=${{ github.token }}
|
||||||
|
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= tee-pot:SS6HcrpG87S1M6HZGPsfo7d1xJccCGev7/tXc5+I4jg=
|
||||||
|
substituters = https://cache.nixos.org/ https://attic.teepot.org/tee-pot
|
||||||
|
sandbox = true
|
||||||
|
- name: Setup Attic cache
|
||||||
|
uses: ryanccn/attic-action@v0
|
||||||
|
with:
|
||||||
|
endpoint: https://attic.teepot.org/
|
||||||
|
cache: tee-pot
|
||||||
|
token: ${{ secrets.ATTIC_TOKEN }}
|
||||||
|
|
||||||
|
- name: nixci
|
||||||
|
# FIXME: this prevents it from running on macos
|
||||||
|
# https://github.com/NixOS/nix/pull/12570
|
||||||
|
# run: nix run github:nixos/nixpkgs/nixos-24.11#nixci -- build
|
||||||
|
run: nix build -L .#teepot
|
||||||
|
|
557
Cargo.lock
generated
557
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,6 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["crates/*", "bin/*", "crates/teepot-vault/bin/*"]
|
members = ["crates/*", "bin/*", "crates/teepot-vault/bin/*"]
|
||||||
|
exclude = ["crates/teepot-tee-quote-verification-rs"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
@ -30,8 +31,6 @@ enumset = { version = "1.1", features = ["serde"] }
|
||||||
getrandom = { version = "0.3.1", features = ["std"] }
|
getrandom = { version = "0.3.1", features = ["std"] }
|
||||||
gpt = "4.0.0"
|
gpt = "4.0.0"
|
||||||
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.3.0" }
|
|
||||||
intel-tee-quote-verification-sys = { version = "0.2.1" }
|
|
||||||
num-integer = "0.1.46"
|
num-integer = "0.1.46"
|
||||||
num-traits = "0.2.18"
|
num-traits = "0.2.18"
|
||||||
opentelemetry = { version = "0.28.0", features = ["default", "logs"] }
|
opentelemetry = { version = "0.28.0", features = ["default", "logs"] }
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use teepot::{
|
use teepot::{
|
||||||
log::{setup_logging, LogLevelParser},
|
log::{setup_logging, LogLevelParser},
|
||||||
tdx::rtmr::UEFI_MARKER_DIGEST_BYTES,
|
tdx::UEFI_MARKER_DIGEST_BYTES,
|
||||||
};
|
};
|
||||||
use tracing::{debug, info, level_filters::LevelFilter};
|
use tracing::{debug, info, level_filters::LevelFilter};
|
||||||
|
|
||||||
|
|
|
@ -6,53 +6,64 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![deny(clippy::all)]
|
#![deny(clippy::all)]
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use tracing::error;
|
||||||
use clap::Parser;
|
|
||||||
use teepot::{
|
|
||||||
log::{setup_logging, LogLevelParser},
|
|
||||||
tdx::rtmr::TdxRtmrEvent,
|
|
||||||
util::pad,
|
|
||||||
};
|
|
||||||
use tracing::{error, level_filters::LevelFilter};
|
|
||||||
|
|
||||||
/// Extend a TDX rtmr with a hash digest for measured boot.
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
#[derive(Parser, Debug)]
|
mod os {
|
||||||
#[command(author, version, about, long_about = None)]
|
use anyhow::{Context as _, Result};
|
||||||
struct Arguments {
|
use clap::Parser;
|
||||||
/// digest in hex to extend the rtmr with
|
use teepot::{
|
||||||
#[arg(long)]
|
log::{setup_logging, LogLevelParser},
|
||||||
digest: String,
|
tdx::rtmr::TdxRtmrEvent,
|
||||||
/// the number or the rtmr
|
util::pad,
|
||||||
#[arg(long, default_value = "2")]
|
};
|
||||||
rtmr: u64,
|
use tracing::level_filters::LevelFilter;
|
||||||
/// Log level for the log output.
|
|
||||||
/// Valid values are: `off`, `error`, `warn`, `info`, `debug`, `trace`
|
/// Extend a TDX rtmr with a hash digest for measured boot.
|
||||||
#[clap(long, default_value_t = LevelFilter::WARN, value_parser = LogLevelParser)]
|
#[derive(Parser, Debug)]
|
||||||
pub log_level: LevelFilter,
|
#[command(author, version, about, long_about = None)]
|
||||||
|
struct Arguments {
|
||||||
|
/// digest in hex to extend the rtmr with
|
||||||
|
#[arg(long)]
|
||||||
|
digest: String,
|
||||||
|
/// the number or the rtmr
|
||||||
|
#[arg(long, default_value = "2")]
|
||||||
|
rtmr: u64,
|
||||||
|
/// Log level for the log output.
|
||||||
|
/// Valid values are: `off`, `error`, `warn`, `info`, `debug`, `trace`
|
||||||
|
#[clap(long, default_value_t = LevelFilter::WARN, value_parser = LogLevelParser)]
|
||||||
|
pub log_level: LevelFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_with_error() -> Result<()> {
|
||||||
|
let args = Arguments::parse();
|
||||||
|
tracing::subscriber::set_global_default(setup_logging(
|
||||||
|
env!("CARGO_CRATE_NAME"),
|
||||||
|
&args.log_level,
|
||||||
|
)?)?;
|
||||||
|
|
||||||
|
// Parse the digest string as a hex array
|
||||||
|
let digest_bytes = hex::decode(&args.digest).context("Invalid digest format")?;
|
||||||
|
let extend_data: [u8; 48] = pad(&digest_bytes).context("Invalid digest length")?;
|
||||||
|
|
||||||
|
// Extend the TDX measurement with the extend data
|
||||||
|
TdxRtmrEvent::default()
|
||||||
|
.with_extend_data(extend_data)
|
||||||
|
.with_rtmr_index(args.rtmr)
|
||||||
|
.extend()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main_with_error() -> Result<()> {
|
#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
|
||||||
let args = Arguments::parse();
|
mod os {
|
||||||
tracing::subscriber::set_global_default(setup_logging(
|
pub fn main_with_error() -> anyhow::Result<()> {
|
||||||
env!("CARGO_CRATE_NAME"),
|
anyhow::bail!("OS or architecture not supported");
|
||||||
&args.log_level,
|
}
|
||||||
)?)?;
|
|
||||||
|
|
||||||
// Parse the digest string as a hex array
|
|
||||||
let digest_bytes = hex::decode(&args.digest).context("Invalid digest format")?;
|
|
||||||
let extend_data: [u8; 48] = pad(&digest_bytes).context("Invalid digest length")?;
|
|
||||||
|
|
||||||
// Extend the TDX measurement with the extend data
|
|
||||||
TdxRtmrEvent::default()
|
|
||||||
.with_extend_data(extend_data)
|
|
||||||
.with_rtmr_index(args.rtmr)
|
|
||||||
.extend()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
fn main() -> Result<()> {
|
let ret = os::main_with_error();
|
||||||
let ret = main_with_error();
|
|
||||||
if let Err(e) = &ret {
|
if let Err(e) = &ret {
|
||||||
error!(error = %e, "Execution failed");
|
error!(error = %e, "Execution failed");
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,21 +6,10 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![deny(clippy::all)]
|
#![deny(clippy::all)]
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use secp256k1::{rand, Secp256k1};
|
use std::ffi::OsString;
|
||||||
use std::{ffi::OsString, os::unix::process::CommandExt, process::Command};
|
|
||||||
use teepot::{
|
|
||||||
ethereum::public_key_to_ethereum_address,
|
|
||||||
prover::reportdata::ReportDataV1,
|
|
||||||
quote::get_quote,
|
|
||||||
tdx::rtmr::{TdxRtmrEvent, UEFI_MARKER_DIGEST_BYTES},
|
|
||||||
};
|
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use tracing_log::LogTracer;
|
|
||||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry};
|
|
||||||
|
|
||||||
const TEE_QUOTE_FILE: &str = "/tmp/tee_quote";
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
|
@ -33,7 +22,21 @@ struct Args {
|
||||||
cmd_args: Vec<OsString>,
|
cmd_args: Vec<OsString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
fn main_with_error() -> Result<()> {
|
fn main_with_error() -> Result<()> {
|
||||||
|
use anyhow::Context;
|
||||||
|
use secp256k1::{rand, Secp256k1};
|
||||||
|
use std::{os::unix::process::CommandExt, process::Command};
|
||||||
|
use teepot::tdx::rtmr::TdxRtmrEvent;
|
||||||
|
use teepot::{
|
||||||
|
ethereum::public_key_to_ethereum_address, prover::reportdata::ReportDataV1,
|
||||||
|
quote::get_quote,
|
||||||
|
};
|
||||||
|
use tracing_log::LogTracer;
|
||||||
|
use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry};
|
||||||
|
|
||||||
|
const TEE_QUOTE_FILE: &str = "/tmp/tee_quote";
|
||||||
|
|
||||||
LogTracer::init().context("Failed to set logger")?;
|
LogTracer::init().context("Failed to set logger")?;
|
||||||
|
|
||||||
let subscriber = Registry::default()
|
let subscriber = Registry::default()
|
||||||
|
@ -54,7 +57,7 @@ fn main_with_error() -> Result<()> {
|
||||||
// so that any breach can't generate a new attestation with the expected RTMRs
|
// so that any breach can't generate a new attestation with the expected RTMRs
|
||||||
TdxRtmrEvent::default()
|
TdxRtmrEvent::default()
|
||||||
.with_rtmr_index(3)
|
.with_rtmr_index(3)
|
||||||
.with_extend_data(UEFI_MARKER_DIGEST_BYTES)
|
.with_extend_data(teepot::tdx::UEFI_MARKER_DIGEST_BYTES)
|
||||||
.extend()?;
|
.extend()?;
|
||||||
|
|
||||||
// save quote to file
|
// save quote to file
|
||||||
|
@ -94,6 +97,11 @@ fn main_with_error() -> Result<()> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
|
||||||
|
fn main_with_error() -> Result<()> {
|
||||||
|
anyhow::bail!("OS or architecture not supported");
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let ret = main_with_error();
|
let ret = main_with_error();
|
||||||
if let Err(e) = &ret {
|
if let Err(e) = &ret {
|
||||||
|
|
|
@ -7,10 +7,7 @@ use anyhow::{bail, Context, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use std::{fs, io::Read, path::PathBuf, str::FromStr, time::UNIX_EPOCH};
|
use std::{fs, io::Read, path::PathBuf, str::FromStr, time::UNIX_EPOCH};
|
||||||
use teepot::quote::{
|
use teepot::quote::{get_collateral, verify_quote_with_collateral, QuoteVerificationResult};
|
||||||
error, tcblevel::TcbLevel, tee_qv_get_collateral, verify_quote_with_collateral,
|
|
||||||
QuoteVerificationResult,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author = "Matter Labs", version, about = "SGX attestation and batch signature verifier", long_about = None)]
|
#[command(author = "Matter Labs", version, about = "SGX attestation and batch signature verifier", long_about = None)]
|
||||||
|
@ -62,10 +59,7 @@ fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result<QuoteVerif
|
||||||
"Verifying quote ({} bytes)...",
|
"Verifying quote ({} bytes)...",
|
||||||
attestation_quote_bytes.len()
|
attestation_quote_bytes.len()
|
||||||
);
|
);
|
||||||
let collateral = error::QuoteContext::context(
|
let collateral = get_collateral(attestation_quote_bytes)?;
|
||||||
tee_qv_get_collateral(attestation_quote_bytes),
|
|
||||||
"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)?
|
||||||
.as_secs() as _;
|
.as_secs() as _;
|
||||||
|
@ -76,7 +70,7 @@ fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result<QuoteVerif
|
||||||
fn print_quote_verification_summary(quote_verification_result: &QuoteVerificationResult) {
|
fn print_quote_verification_summary(quote_verification_result: &QuoteVerificationResult) {
|
||||||
let QuoteVerificationResult {
|
let QuoteVerificationResult {
|
||||||
collateral_expired,
|
collateral_expired,
|
||||||
result,
|
result: tcblevel,
|
||||||
quote,
|
quote,
|
||||||
advisories,
|
advisories,
|
||||||
..
|
..
|
||||||
|
@ -84,7 +78,6 @@ fn print_quote_verification_summary(quote_verification_result: &QuoteVerificatio
|
||||||
if *collateral_expired {
|
if *collateral_expired {
|
||||||
println!("Freshly fetched collateral expired");
|
println!("Freshly fetched collateral expired");
|
||||||
}
|
}
|
||||||
let tcblevel = TcbLevel::from(*result);
|
|
||||||
for advisory in advisories {
|
for advisory in advisories {
|
||||||
println!("\tInfo: Advisory ID: {advisory}");
|
println!("\tInfo: Advisory ID: {advisory}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,12 +61,12 @@ impl Proof {
|
||||||
pub fn is_permanently_ignored(&self) -> bool {
|
pub fn is_permanently_ignored(&self) -> bool {
|
||||||
self.status
|
self.status
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(false, |s| s.eq_ignore_ascii_case("permanently_ignored"))
|
.is_some_and(|s| s.eq_ignore_ascii_case("permanently_ignored"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the proof is failed or picked by a prover
|
/// Check if the proof is failed or picked by a prover
|
||||||
pub fn is_failed_or_picked(&self) -> bool {
|
pub fn is_failed_or_picked(&self) -> bool {
|
||||||
self.status.as_ref().map_or(false, |s| {
|
self.status.as_ref().is_some_and(|s| {
|
||||||
s.eq_ignore_ascii_case("failed") || s.eq_ignore_ascii_case("picked_by_prover")
|
s.eq_ignore_ascii_case("failed") || s.eq_ignore_ascii_case("picked_by_prover")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (c) 2023-2025 Matter Labs
|
// Copyright (c) 2023-2025 Matter Labs
|
||||||
|
|
||||||
use teepot::quote::{
|
use teepot::quote::{get_collateral, verify_quote_with_collateral, QuoteVerificationResult};
|
||||||
error::QuoteContext, tee_qv_get_collateral, verify_quote_with_collateral,
|
|
||||||
QuoteVerificationResult,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::error;
|
use crate::error;
|
||||||
|
|
||||||
|
@ -15,10 +12,7 @@ impl AttestationVerifier {
|
||||||
/// Verify an attestation quote
|
/// Verify an attestation quote
|
||||||
pub fn verify_quote(attestation_quote_bytes: &[u8]) -> error::Result<QuoteVerificationResult> {
|
pub fn verify_quote(attestation_quote_bytes: &[u8]) -> error::Result<QuoteVerificationResult> {
|
||||||
// Get collateral for the quote
|
// Get collateral for the quote
|
||||||
let collateral = QuoteContext::context(
|
let collateral = get_collateral(attestation_quote_bytes)?;
|
||||||
tee_qv_get_collateral(attestation_quote_bytes),
|
|
||||||
"Failed to get collateral!",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Get current time for verification
|
// Get current time for verification
|
||||||
let unix_time: i64 = std::time::SystemTime::now()
|
let unix_time: i64 = std::time::SystemTime::now()
|
||||||
|
|
|
@ -19,7 +19,7 @@ impl PolicyEnforcer {
|
||||||
quote_verification_result: &QuoteVerificationResult,
|
quote_verification_result: &QuoteVerificationResult,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let quote = "e_verification_result.quote;
|
let quote = "e_verification_result.quote;
|
||||||
let tcblevel = TcbLevel::from(quote_verification_result.result);
|
let tcblevel = quote_verification_result.result;
|
||||||
|
|
||||||
match "e.report {
|
match "e.report {
|
||||||
Report::SgxEnclave(report_body) => {
|
Report::SgxEnclave(report_body) => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (c) 2023-2025 Matter Labs
|
// Copyright (c) 2023-2025 Matter Labs
|
||||||
|
|
||||||
use teepot::quote::{tcblevel::TcbLevel, QuoteVerificationResult};
|
use teepot::quote::QuoteVerificationResult;
|
||||||
|
|
||||||
/// Handles reporting and logging of verification results
|
/// Handles reporting and logging of verification results
|
||||||
pub struct VerificationReporter;
|
pub struct VerificationReporter;
|
||||||
|
@ -11,7 +11,7 @@ impl VerificationReporter {
|
||||||
pub fn log_quote_verification_summary(quote_verification_result: &QuoteVerificationResult) {
|
pub fn log_quote_verification_summary(quote_verification_result: &QuoteVerificationResult) {
|
||||||
let QuoteVerificationResult {
|
let QuoteVerificationResult {
|
||||||
collateral_expired,
|
collateral_expired,
|
||||||
result,
|
result: tcblevel,
|
||||||
quote,
|
quote,
|
||||||
advisories,
|
advisories,
|
||||||
..
|
..
|
||||||
|
@ -21,7 +21,6 @@ impl VerificationReporter {
|
||||||
tracing::warn!("Freshly fetched collateral expired!");
|
tracing::warn!("Freshly fetched collateral expired!");
|
||||||
}
|
}
|
||||||
|
|
||||||
let tcblevel = TcbLevel::from(*result);
|
|
||||||
let advisories = if advisories.is_empty() {
|
let advisories = if advisories.is_empty() {
|
||||||
"None".to_string()
|
"None".to_string()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,10 +5,12 @@ name = "teepot-tee-quote-verification-rs"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "BSD-3-Clause"
|
license = "BSD-3-Clause"
|
||||||
repository.workspace = true
|
repository = "https://github.com/matter-labs/teepot"
|
||||||
homepage.workspace = true
|
homepage = "https://github.com/matter-labs/teepot"
|
||||||
description = "Fork of intel-tee-quote-verification-rs"
|
description = "Fork of intel-tee-quote-verification-rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
intel-tee-quote-verification-sys.workspace = true
|
serde = { version = "1", features = ["derive", "rc"] }
|
||||||
serde.workspace = true
|
|
||||||
|
[target.'cfg(all(target_os = "linux", target_arch = "x86_64"))'.dependencies]
|
||||||
|
intel-tee-quote-verification-sys = { version = "0.2.1" }
|
||||||
|
|
|
@ -17,14 +17,12 @@ clap.workspace = true
|
||||||
const-oid.workspace = true
|
const-oid.workspace = true
|
||||||
futures-core = { version = "0.3.30", features = ["alloc"], default-features = false }
|
futures-core = { version = "0.3.30", features = ["alloc"], default-features = false }
|
||||||
hex.workspace = true
|
hex.workspace = true
|
||||||
intel-tee-quote-verification-rs.workspace = true
|
|
||||||
pgp.workspace = true
|
pgp.workspace = true
|
||||||
rustls.workspace = true
|
rustls.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
serde_with.workspace = true
|
serde_with.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
tdx-attest-rs.workspace = true
|
|
||||||
teepot.workspace = true
|
teepot.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
|
|
|
@ -8,13 +8,15 @@
|
||||||
|
|
||||||
pub mod vault;
|
pub mod vault;
|
||||||
|
|
||||||
use crate::server::pki::{RaTlsCollateralExtension, RaTlsQuoteExtension};
|
use crate::server::{
|
||||||
|
attestation::Collateral,
|
||||||
|
pki::{RaTlsCollateralExtension, RaTlsQuoteExtension},
|
||||||
|
};
|
||||||
use actix_web::http::header;
|
use actix_web::http::header;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use awc::{Client, Connector};
|
use awc::{Client, Connector};
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use const_oid::AssociatedOid;
|
use const_oid::AssociatedOid;
|
||||||
use intel_tee_quote_verification_rs::Collateral;
|
|
||||||
use rustls::{
|
use rustls::{
|
||||||
client::{
|
client::{
|
||||||
danger::{HandshakeSignatureValid, ServerCertVerifier},
|
danger::{HandshakeSignatureValid, ServerCertVerifier},
|
||||||
|
@ -25,10 +27,9 @@ use rustls::{
|
||||||
};
|
};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::{sync::Arc, time, time::Duration};
|
use std::{sync::Arc, time, time::Duration};
|
||||||
pub use teepot::{
|
pub use teepot::quote::{
|
||||||
quote::tcblevel::{parse_tcb_levels, EnumSet, TcbLevel},
|
tcblevel::{parse_tcb_levels, EnumSet, TcbLevel},
|
||||||
quote::{verify_quote_with_collateral, QuoteVerificationResult},
|
verify_quote_with_collateral, QuoteVerificationResult,
|
||||||
sgx::sgx_ql_qv_result_t,
|
|
||||||
};
|
};
|
||||||
use teepot::{quote::Report, sgx::Quote};
|
use teepot::{quote::Report, sgx::Quote};
|
||||||
use tracing::{debug, error, info, trace, warn};
|
use tracing::{debug, error, info, trace, warn};
|
||||||
|
@ -194,7 +195,7 @@ impl TeeConnection {
|
||||||
|
|
||||||
let QuoteVerificationResult {
|
let QuoteVerificationResult {
|
||||||
collateral_expired,
|
collateral_expired,
|
||||||
result,
|
result: tcblevel,
|
||||||
quote,
|
quote,
|
||||||
advisories,
|
advisories,
|
||||||
earliest_expiration_date,
|
earliest_expiration_date,
|
||||||
|
@ -206,7 +207,7 @@ impl TeeConnection {
|
||||||
return Err(Error::General("TDX quote and not SGX quote".into()));
|
return Err(Error::General("TDX quote and not SGX quote".into()));
|
||||||
};
|
};
|
||||||
|
|
||||||
if collateral_expired || result != sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK {
|
if collateral_expired || tcblevel != TcbLevel::Ok {
|
||||||
if collateral_expired {
|
if collateral_expired {
|
||||||
error!(
|
error!(
|
||||||
"Collateral is out of date! Expired {}",
|
"Collateral is out of date! Expired {}",
|
||||||
|
@ -218,11 +219,10 @@ impl TeeConnection {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let tcblevel = TcbLevel::from(result);
|
|
||||||
if self
|
if self
|
||||||
.args
|
.args
|
||||||
.sgx_allowed_tcb_levels
|
.sgx_allowed_tcb_levels
|
||||||
.map_or(true, |levels| !levels.contains(tcblevel))
|
.is_none_or(|levels| !levels.contains(tcblevel))
|
||||||
{
|
{
|
||||||
error!("Quote verification result: {}", tcblevel);
|
error!("Quote verification result: {}", tcblevel);
|
||||||
return Err(Error::General(format!(
|
return Err(Error::General(format!(
|
||||||
|
|
|
@ -20,7 +20,6 @@ use awc::{
|
||||||
};
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
|
||||||
use rustls::ClientConfig;
|
use rustls::ClientConfig;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -28,13 +27,13 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time,
|
time,
|
||||||
};
|
};
|
||||||
use teepot::quote::error::QuoteContext;
|
use teepot::quote::get_collateral;
|
||||||
pub use teepot::{
|
pub use teepot::{
|
||||||
quote::{
|
quote::{
|
||||||
tcblevel::{parse_tcb_levels, EnumSet, TcbLevel},
|
tcblevel::{parse_tcb_levels, EnumSet, TcbLevel},
|
||||||
verify_quote_with_collateral, QuoteVerificationResult,
|
verify_quote_with_collateral, QuoteVerificationResult,
|
||||||
},
|
},
|
||||||
sgx::{sgx_gramine_get_quote, sgx_ql_qv_result_t, Collateral},
|
sgx::{sgx_gramine_get_quote, Collateral},
|
||||||
};
|
};
|
||||||
use tracing::{debug, error, info, trace};
|
use tracing::{debug, error, info, trace};
|
||||||
|
|
||||||
|
@ -158,7 +157,7 @@ impl VaultConnection {
|
||||||
info!("Getting attestation report");
|
info!("Getting attestation report");
|
||||||
let attestation_url = AuthRequest::URL;
|
let attestation_url = AuthRequest::URL;
|
||||||
let quote = sgx_gramine_get_quote(&self.key_hash).context("Failed to get own quote")?;
|
let quote = sgx_gramine_get_quote(&self.key_hash).context("Failed to get own quote")?;
|
||||||
let collateral = tee_qv_get_collateral("e).context("Failed to get own collateral")?;
|
let collateral = get_collateral("e).context("Failed to get own collateral")?;
|
||||||
|
|
||||||
let auth_req = AuthRequest {
|
let auth_req = AuthRequest {
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
|
@ -298,7 +297,7 @@ impl VaultConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// set a secret in the vault
|
/// set a secret in the vault
|
||||||
pub async fn store_secret<'de, T: serde::Serialize>(
|
pub async fn store_secret<T: serde::Serialize>(
|
||||||
&self,
|
&self,
|
||||||
val: T,
|
val: T,
|
||||||
rel_path: &str,
|
rel_path: &str,
|
||||||
|
@ -307,7 +306,7 @@ impl VaultConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// set a secret in the vault for a different TEE
|
/// set a secret in the vault for a different TEE
|
||||||
pub async fn store_secret_for_tee<'de, T: serde::Serialize>(
|
pub async fn store_secret_for_tee<T: serde::Serialize>(
|
||||||
&self,
|
&self,
|
||||||
tee_name: &str,
|
tee_name: &str,
|
||||||
val: T,
|
val: T,
|
||||||
|
@ -331,7 +330,7 @@ impl VaultConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get a secret from the vault
|
/// get a secret from the vault
|
||||||
pub async fn load_secret<'de, T: serde::de::DeserializeOwned>(
|
pub async fn load_secret<T: serde::de::DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
rel_path: &str,
|
rel_path: &str,
|
||||||
) -> Result<Option<T>, HttpResponseError> {
|
) -> Result<Option<T>, HttpResponseError> {
|
||||||
|
@ -339,7 +338,7 @@ impl VaultConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get a secret from the vault for a specific TEE
|
/// get a secret from the vault for a specific TEE
|
||||||
pub async fn load_secret_for_tee<'de, T: serde::de::DeserializeOwned>(
|
pub async fn load_secret_for_tee<T: serde::de::DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
tee_name: &str,
|
tee_name: &str,
|
||||||
rel_path: &str,
|
rel_path: &str,
|
||||||
|
|
|
@ -10,6 +10,15 @@ edition.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|
||||||
|
[target.'cfg(all(target_os = "linux", target_arch = "x86_64"))'.dependencies]
|
||||||
|
tdx-attest-rs = { version = "0.1.2", git = "https://github.com/intel/SGXDataCenterAttestationPrimitives.git", rev = "aa239d25a437a28f3f4de92c38f5b6809faac842" }
|
||||||
|
teepot-tee-quote-verification-rs = { path = "../teepot-tee-quote-verification-rs" }
|
||||||
|
|
||||||
|
[target.'cfg(not(all(target_os = "linux", target_arch = "x86_64")))'.dependencies]
|
||||||
|
dcap-qvl = "0.2.3"
|
||||||
|
chrono = "0.4.40"
|
||||||
|
bytes.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
|
@ -18,9 +27,9 @@ clap.workspace = true
|
||||||
config.workspace = true
|
config.workspace = true
|
||||||
const-oid.workspace = true
|
const-oid.workspace = true
|
||||||
enumset.workspace = true
|
enumset.workspace = true
|
||||||
|
futures = "0.3.31"
|
||||||
getrandom.workspace = true
|
getrandom.workspace = true
|
||||||
hex.workspace = true
|
hex.workspace = true
|
||||||
intel-tee-quote-verification-rs.workspace = true
|
|
||||||
num-integer.workspace = true
|
num-integer.workspace = true
|
||||||
num-traits.workspace = true
|
num-traits.workspace = true
|
||||||
opentelemetry.workspace = true
|
opentelemetry.workspace = true
|
||||||
|
@ -39,8 +48,8 @@ serde_json.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
sha3.workspace = true
|
sha3.workspace = true
|
||||||
signature.workspace = true
|
signature.workspace = true
|
||||||
tdx-attest-rs.workspace = true
|
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracing-futures.workspace = true
|
tracing-futures.workspace = true
|
||||||
tracing-log.workspace = true
|
tracing-log.workspace = true
|
||||||
|
|
|
@ -2,13 +2,12 @@
|
||||||
// Copyright (c) 2023-2025 Matter Labs
|
// Copyright (c) 2023-2025 Matter Labs
|
||||||
|
|
||||||
//! Create a private key and a signed and self-signed certificates
|
//! Create a private key and a signed and self-signed certificates
|
||||||
use crate::quote::{error::QuoteContext, get_quote};
|
use crate::quote::{get_collateral, get_quote};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use const_oid::{
|
use const_oid::{
|
||||||
db::rfc5280::{ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH},
|
db::rfc5280::{ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH},
|
||||||
AssociatedOid,
|
AssociatedOid,
|
||||||
};
|
};
|
||||||
use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
|
||||||
use p256::{ecdsa::DerSignature, pkcs8::EncodePrivateKey};
|
use p256::{ecdsa::DerSignature, pkcs8::EncodePrivateKey};
|
||||||
use pkcs8::der;
|
use pkcs8::der;
|
||||||
use rustls::pki_types::PrivatePkcs8KeyDer;
|
use rustls::pki_types::PrivatePkcs8KeyDer;
|
||||||
|
@ -148,7 +147,7 @@ pub fn make_self_signed_cert(
|
||||||
debug!("quote.len: {:?}", quote.len());
|
debug!("quote.len: {:?}", quote.len());
|
||||||
// Create a relative distinguished name.
|
// Create a relative distinguished name.
|
||||||
let rdns = RdnSequence::from_str(dn)?;
|
let rdns = RdnSequence::from_str(dn)?;
|
||||||
let collateral = tee_qv_get_collateral("e).context("Failed to get own collateral")?;
|
let collateral = get_collateral("e).context("Failed to get own collateral")?;
|
||||||
|
|
||||||
let mut serial = [0u8; 16];
|
let mut serial = [0u8; 16];
|
||||||
getrandom::fill(&mut serial)?;
|
getrandom::fill(&mut serial)?;
|
||||||
|
|
|
@ -4,13 +4,11 @@
|
||||||
//! Common attestation API for all TEEs
|
//! Common attestation API for all TEEs
|
||||||
|
|
||||||
use crate::quote::{
|
use crate::quote::{
|
||||||
error::QuoteContext,
|
get_collateral, get_quote,
|
||||||
get_quote,
|
|
||||||
tcblevel::{EnumSet, TcbLevel},
|
tcblevel::{EnumSet, TcbLevel},
|
||||||
verify_quote_with_collateral, Collateral, QuoteVerificationResult,
|
verify_quote_with_collateral, Collateral, QuoteVerificationResult,
|
||||||
};
|
};
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
|
@ -46,8 +44,7 @@ pub fn get_quote_and_collateral(
|
||||||
static ATTESTATION: RwLock<Option<Attestation>> = RwLock::new(None);
|
static ATTESTATION: RwLock<Option<Attestation>> = RwLock::new(None);
|
||||||
|
|
||||||
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 _;
|
||||||
|
|
||||||
if let Some(attestation) = ATTESTATION.read().unwrap().as_ref() {
|
if let Some(attestation) = ATTESTATION.read().unwrap().as_ref() {
|
||||||
|
@ -65,11 +62,11 @@ pub fn get_quote_and_collateral(
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_tee_type, myquote) = get_quote(report_data).context("Failed to get own quote")?;
|
let (_tee_type, myquote) = get_quote(report_data).context("Failed to get own quote")?;
|
||||||
let collateral = tee_qv_get_collateral(&myquote).context("Failed to get own collateral")?;
|
let collateral = get_collateral(&myquote).context("Failed to get own collateral")?;
|
||||||
|
|
||||||
let QuoteVerificationResult {
|
let QuoteVerificationResult {
|
||||||
collateral_expired,
|
collateral_expired,
|
||||||
result,
|
result: tcblevel,
|
||||||
earliest_expiration_date,
|
earliest_expiration_date,
|
||||||
tcb_level_date_tag,
|
tcb_level_date_tag,
|
||||||
quote,
|
quote,
|
||||||
|
@ -83,9 +80,8 @@ pub fn get_quote_and_collateral(
|
||||||
bail!("Freshly fetched collateral expired");
|
bail!("Freshly fetched collateral expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
let tcblevel = TcbLevel::from(result);
|
|
||||||
if tcblevel != TcbLevel::Ok
|
if tcblevel != TcbLevel::Ok
|
||||||
&& allowed_tcb_levels.map_or(false, |levels| !levels.contains(tcblevel))
|
&& allowed_tcb_levels.is_some_and(|levels| !levels.contains(tcblevel))
|
||||||
{
|
{
|
||||||
error!("Quote verification result: {}", tcblevel);
|
error!("Quote verification result: {}", tcblevel);
|
||||||
bail!("Quote verification result: {}", tcblevel);
|
bail!("Quote verification result: {}", tcblevel);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (c) 2024 Matter Labs
|
// Copyright (c) 2024-2025 Matter Labs
|
||||||
|
|
||||||
//! Quote Error type
|
//! Quote Error type
|
||||||
|
|
||||||
use intel_tee_quote_verification_rs::quote3_error_t;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
use tdx_attest_rs::tdx_attest_error_t;
|
use tdx_attest_rs::tdx_attest_error_t;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -22,8 +22,7 @@ pub enum QuoteError {
|
||||||
InvalidTeeType,
|
InvalidTeeType,
|
||||||
#[error("unsupported body type")]
|
#[error("unsupported body type")]
|
||||||
UnsupportedBodyType,
|
UnsupportedBodyType,
|
||||||
#[error("quote verification error {msg}: {inner:?}")]
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
Quote3Error { inner: quote3_error_t, msg: String },
|
|
||||||
#[error("tdx_att_get_quote error {msg}: {inner:?}")]
|
#[error("tdx_att_get_quote error {msg}: {inner:?}")]
|
||||||
TdxAttGetQuote {
|
TdxAttGetQuote {
|
||||||
inner: tdx_attest_error_t,
|
inner: tdx_attest_error_t,
|
||||||
|
@ -35,8 +34,31 @@ pub enum QuoteError {
|
||||||
ReportDataSize,
|
ReportDataSize,
|
||||||
#[error("can't get a quote: unknown TEE")]
|
#[error("can't get a quote: unknown TEE")]
|
||||||
UnknownTee,
|
UnknownTee,
|
||||||
|
#[error("{0}: invalid parameter")]
|
||||||
|
InvalidParameter(String),
|
||||||
|
#[error("{0}: platform lib unavailable")]
|
||||||
|
PlatformLibUnavailable(String),
|
||||||
|
#[error("{0}: pck cert chain error")]
|
||||||
|
PckCertChainError(String),
|
||||||
|
#[error("{0}: pck cert unsupported format")]
|
||||||
|
PckCertUnsupportedFormat(String),
|
||||||
|
#[error("{0}: quote format unsupported")]
|
||||||
|
QuoteFormatUnsupported(String),
|
||||||
|
#[error("{0}: out of memory")]
|
||||||
|
OutOfMemory(String),
|
||||||
|
#[error("{0}: no quote collateral data")]
|
||||||
|
NoQuoteCollateralData(String),
|
||||||
|
#[error("{0}: unexpected error")]
|
||||||
|
Unexpected(String),
|
||||||
|
#[error("{0}: quote certification data unsupported")]
|
||||||
|
QuoteCertificationDataUnsupported(String),
|
||||||
|
#[error("{0}: unable to generate report")]
|
||||||
|
UnableToGenerateReport(String),
|
||||||
|
#[error("{0}: CRL unsupported format")]
|
||||||
|
CrlUnsupportedFormat(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
impl From<tdx_attest_error_t> for QuoteError {
|
impl From<tdx_attest_error_t> for QuoteError {
|
||||||
fn from(code: tdx_attest_error_t) -> Self {
|
fn from(code: tdx_attest_error_t) -> Self {
|
||||||
Self::TdxAttGetQuote {
|
Self::TdxAttGetQuote {
|
||||||
|
@ -64,16 +86,29 @@ impl<T> QuoteContext for Result<T, std::io::Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> QuoteContext for Result<T, quote3_error_t> {
|
impl<T> QuoteContext for Option<T> {
|
||||||
type Ok = T;
|
type Ok = T;
|
||||||
fn context<I: Into<String>>(self, msg: I) -> Result<T, QuoteError> {
|
fn context<I: Into<String>>(self, msg: I) -> Result<T, QuoteError> {
|
||||||
self.map_err(|e| QuoteError::Quote3Error {
|
self.ok_or(QuoteError::Unexpected(msg.into()))
|
||||||
msg: msg.into(),
|
|
||||||
inner: e,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Usability trait for easy QuoteError annotation
|
||||||
|
pub trait QuoteContextErr {
|
||||||
|
/// The Ok Type
|
||||||
|
type Ok;
|
||||||
|
/// The Context
|
||||||
|
fn str_context<I: std::fmt::Display>(self, msg: I) -> Result<Self::Ok, QuoteError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E: std::fmt::Display> QuoteContextErr for Result<T, E> {
|
||||||
|
type Ok = T;
|
||||||
|
fn str_context<I: std::fmt::Display>(self, msg: I) -> Result<T, QuoteError> {
|
||||||
|
self.map_err(|e| QuoteError::Unexpected(format!("{}: {}", msg, e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
impl<T> QuoteContext for Result<T, tdx_attest_error_t> {
|
impl<T> QuoteContext for Result<T, tdx_attest_error_t> {
|
||||||
type Ok = T;
|
type Ok = T;
|
||||||
fn context<I: Into<String>>(self, msg: I) -> Result<T, QuoteError> {
|
fn context<I: Into<String>>(self, msg: I) -> Result<T, QuoteError> {
|
||||||
|
|
260
crates/teepot/src/quote/intel.rs
Normal file
260
crates/teepot/src/quote/intel.rs
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2025 Matter Labs
|
||||||
|
|
||||||
|
//! Drive Intel DCAP verification crate, which is using the C library
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
quote::{
|
||||||
|
error::QuoteError, tcblevel::TcbLevel, Collateral, Quote, QuoteVerificationResult, TEEType,
|
||||||
|
},
|
||||||
|
sgx::sgx_gramine_get_quote,
|
||||||
|
};
|
||||||
|
use bytemuck::cast_slice;
|
||||||
|
use std::{ffi::CStr, mem, mem::MaybeUninit, pin::Pin};
|
||||||
|
use tdx_attest_rs::{tdx_att_get_quote, tdx_attest_error_t, tdx_report_data_t};
|
||||||
|
use teepot_tee_quote_verification_rs::{
|
||||||
|
quote3_error_t as _quote3_error_t, sgx_ql_qv_result_t, sgx_ql_qv_supplemental_t,
|
||||||
|
tee_get_supplemental_data_version_and_size, tee_qv_get_collateral, tee_supp_data_descriptor_t,
|
||||||
|
tee_verify_quote, Collateral as IntelCollateral,
|
||||||
|
};
|
||||||
|
use tracing::{trace, warn};
|
||||||
|
|
||||||
|
/// Convert SGX QV result to our TcbLevel enum
|
||||||
|
fn convert_tcb_level(value: sgx_ql_qv_result_t) -> TcbLevel {
|
||||||
|
match value {
|
||||||
|
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => TcbLevel::Ok,
|
||||||
|
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE => TcbLevel::OutOfDate,
|
||||||
|
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED => {
|
||||||
|
TcbLevel::OutOfDateConfigNeeded
|
||||||
|
}
|
||||||
|
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED => TcbLevel::SwHardeningNeeded,
|
||||||
|
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => {
|
||||||
|
TcbLevel::ConfigAndSwHardeningNeeded
|
||||||
|
}
|
||||||
|
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_NEEDED => TcbLevel::ConfigNeeded,
|
||||||
|
_ => TcbLevel::Invalid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert quote3_error_t to QuoteError with context
|
||||||
|
fn convert_quote_error(error: _quote3_error_t, context: impl Into<String>) -> QuoteError {
|
||||||
|
let context = context.into();
|
||||||
|
match error {
|
||||||
|
_quote3_error_t::SGX_QL_ERROR_INVALID_PARAMETER => QuoteError::InvalidParameter(context),
|
||||||
|
_quote3_error_t::SGX_QL_PCK_CERT_CHAIN_ERROR => QuoteError::PckCertChainError(context),
|
||||||
|
_quote3_error_t::SGX_QL_PCK_CERT_UNSUPPORTED_FORMAT => {
|
||||||
|
QuoteError::PckCertUnsupportedFormat(context)
|
||||||
|
}
|
||||||
|
_quote3_error_t::SGX_QL_QUOTE_FORMAT_UNSUPPORTED => {
|
||||||
|
QuoteError::QuoteFormatUnsupported(context)
|
||||||
|
}
|
||||||
|
_quote3_error_t::SGX_QL_ERROR_OUT_OF_MEMORY => QuoteError::OutOfMemory(context),
|
||||||
|
_quote3_error_t::SGX_QL_NO_QUOTE_COLLATERAL_DATA => {
|
||||||
|
QuoteError::NoQuoteCollateralData(context)
|
||||||
|
}
|
||||||
|
_quote3_error_t::SGX_QL_ERROR_UNEXPECTED => QuoteError::Unexpected(context),
|
||||||
|
_quote3_error_t::SGX_QL_QUOTE_CERTIFICATION_DATA_UNSUPPORTED => {
|
||||||
|
QuoteError::QuoteCertificationDataUnsupported(context)
|
||||||
|
}
|
||||||
|
_quote3_error_t::SGX_QL_UNABLE_TO_GENERATE_REPORT => {
|
||||||
|
QuoteError::UnableToGenerateReport(context)
|
||||||
|
}
|
||||||
|
_quote3_error_t::SGX_QL_CRL_UNSUPPORTED_FORMAT => QuoteError::CrlUnsupportedFormat(context),
|
||||||
|
_ => QuoteError::Unexpected(context),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert internal Collateral type to Intel library Collateral type
|
||||||
|
fn convert_to_intel_collateral(collateral: &Collateral) -> IntelCollateral {
|
||||||
|
IntelCollateral {
|
||||||
|
major_version: collateral.major_version,
|
||||||
|
minor_version: collateral.minor_version,
|
||||||
|
tee_type: collateral.tee_type,
|
||||||
|
pck_crl_issuer_chain: collateral.pck_crl_issuer_chain.clone(),
|
||||||
|
root_ca_crl: collateral.root_ca_crl.clone(),
|
||||||
|
pck_crl: collateral.pck_crl.clone(),
|
||||||
|
tcb_info_issuer_chain: collateral.tcb_info_issuer_chain.clone(),
|
||||||
|
tcb_info: collateral.tcb_info.clone(),
|
||||||
|
qe_identity_issuer_chain: collateral.qe_identity_issuer_chain.clone(),
|
||||||
|
qe_identity: collateral.qe_identity.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get collateral data for a quote
|
||||||
|
pub fn get_collateral(quote: &[u8]) -> Result<Collateral, QuoteError> {
|
||||||
|
let collateral = tee_qv_get_collateral(quote)
|
||||||
|
.map_err(|e| convert_quote_error(e, "tee_qv_get_collateral"))?;
|
||||||
|
|
||||||
|
Ok(Collateral {
|
||||||
|
major_version: collateral.major_version,
|
||||||
|
minor_version: collateral.minor_version,
|
||||||
|
tee_type: collateral.tee_type,
|
||||||
|
pck_crl_issuer_chain: collateral.pck_crl_issuer_chain,
|
||||||
|
root_ca_crl: collateral.root_ca_crl,
|
||||||
|
pck_crl: collateral.pck_crl,
|
||||||
|
tcb_info_issuer_chain: collateral.tcb_info_issuer_chain,
|
||||||
|
tcb_info: collateral.tcb_info,
|
||||||
|
qe_identity_issuer_chain: collateral.qe_identity_issuer_chain,
|
||||||
|
qe_identity: collateral.qe_identity,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract advisory information from supplemental data
|
||||||
|
fn extract_supplemental_data_info(supp_data: sgx_ql_qv_supplemental_t) -> (Vec<String>, i64, i64) {
|
||||||
|
// Convert to valid UTF-8 string
|
||||||
|
let advisories = CStr::from_bytes_until_nul(cast_slice(&supp_data.sa_list[..]))
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| CStr::to_str(s).ok())
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|s| s.split(',').map(str::trim).map(String::from))
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
(
|
||||||
|
advisories,
|
||||||
|
supp_data.earliest_expiration_date,
|
||||||
|
supp_data.tcb_level_date_tag,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies a quote with optional collateral material
|
||||||
|
pub(crate) fn verify_quote_with_collateral(
|
||||||
|
quote: &[u8],
|
||||||
|
collateral: Option<&crate::quote::Collateral>,
|
||||||
|
current_time: i64,
|
||||||
|
) -> Result<QuoteVerificationResult, QuoteError> {
|
||||||
|
// Convert collateral if provided
|
||||||
|
let intel_collateral = collateral.map(convert_to_intel_collateral);
|
||||||
|
|
||||||
|
let TeeSuppDataDescriptor {
|
||||||
|
supp_data,
|
||||||
|
mut supp_data_descriptor,
|
||||||
|
} = initialize_supplemental_data(quote)?;
|
||||||
|
|
||||||
|
let (collateral_expiration_status, result) = tee_verify_quote(
|
||||||
|
quote,
|
||||||
|
intel_collateral.as_ref(),
|
||||||
|
current_time,
|
||||||
|
None,
|
||||||
|
supp_data_descriptor.as_mut(),
|
||||||
|
)
|
||||||
|
.map_err(|e| convert_quote_error(e, "tee_verify_quote"))?;
|
||||||
|
|
||||||
|
// Extract supplemental data if available
|
||||||
|
let (advisories, earliest_expiration_date, tcb_level_date_tag) =
|
||||||
|
if supp_data_descriptor.is_some() {
|
||||||
|
let supp_data = unsafe { supp_data.assume_init() };
|
||||||
|
extract_supplemental_data_info(supp_data)
|
||||||
|
} else {
|
||||||
|
(vec![], 0, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
let quote = Quote::parse(quote)?;
|
||||||
|
|
||||||
|
Ok(QuoteVerificationResult {
|
||||||
|
collateral_expired: collateral_expiration_status != 0,
|
||||||
|
earliest_expiration_date,
|
||||||
|
tcb_level_date_tag,
|
||||||
|
result: convert_tcb_level(result),
|
||||||
|
quote,
|
||||||
|
advisories,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TeeSuppDataDescriptor {
|
||||||
|
supp_data: Pin<Box<MaybeUninit<sgx_ql_qv_supplemental_t>>>,
|
||||||
|
supp_data_descriptor: Option<tee_supp_data_descriptor_t>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialize_supplemental_data(quote: &[u8]) -> Result<TeeSuppDataDescriptor, QuoteError> {
|
||||||
|
let mut supp_data = Box::pin(mem::MaybeUninit::zeroed());
|
||||||
|
let mut supp_data_desc = tee_supp_data_descriptor_t {
|
||||||
|
major_version: 0,
|
||||||
|
data_size: 0,
|
||||||
|
p_data: supp_data.as_mut_ptr() as *mut u8,
|
||||||
|
};
|
||||||
|
trace!("tee_get_supplemental_data_version_and_size");
|
||||||
|
let (_, supp_size) = tee_get_supplemental_data_version_and_size(quote)
|
||||||
|
.map_err(|e| convert_quote_error(e, "tee_get_supplemental_data_version_and_size"))?;
|
||||||
|
|
||||||
|
trace!(
|
||||||
|
"tee_get_supplemental_data_version_and_size supp_size: {}",
|
||||||
|
supp_size
|
||||||
|
);
|
||||||
|
|
||||||
|
let p_supplemental_data = if supp_size == mem::size_of::<sgx_ql_qv_supplemental_t>() as u32 {
|
||||||
|
supp_data_desc.data_size = supp_size;
|
||||||
|
Some(supp_data_desc)
|
||||||
|
} else {
|
||||||
|
supp_data_desc.data_size = 0;
|
||||||
|
trace!(
|
||||||
|
"tee_get_supplemental_data_version_and_size supp_size: {}",
|
||||||
|
supp_size
|
||||||
|
);
|
||||||
|
trace!(
|
||||||
|
"mem::size_of::<sgx_ql_qv_supplemental_t>(): {}",
|
||||||
|
mem::size_of::<sgx_ql_qv_supplemental_t>()
|
||||||
|
);
|
||||||
|
warn!("Quote supplemental data size is different between DCAP QVL and QvE, please make sure you install DCAP QVL and QvE from the same release.");
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(TeeSuppDataDescriptor {
|
||||||
|
supp_data,
|
||||||
|
supp_data_descriptor: p_supplemental_data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepare report data for quote generation
|
||||||
|
fn prepare_report_data(report_data: &[u8]) -> Result<[u8; 64], QuoteError> {
|
||||||
|
if report_data.len() > 64 {
|
||||||
|
return Err(QuoteError::ReportDataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut report_data_fixed = [0u8; 64];
|
||||||
|
report_data_fixed[..report_data.len()].copy_from_slice(report_data);
|
||||||
|
Ok(report_data_fixed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a TDX quote
|
||||||
|
fn tdx_get_quote(report_data_bytes: &[u8; 64]) -> Result<Box<[u8]>, QuoteError> {
|
||||||
|
let mut tdx_report_data = tdx_report_data_t { d: [0; 64usize] };
|
||||||
|
tdx_report_data.d.copy_from_slice(report_data_bytes);
|
||||||
|
|
||||||
|
let (error, quote) = tdx_att_get_quote(Some(&tdx_report_data), None, None, 0);
|
||||||
|
|
||||||
|
if error == tdx_attest_error_t::TDX_ATTEST_SUCCESS {
|
||||||
|
if let Some(quote) = quote {
|
||||||
|
Ok(quote.into())
|
||||||
|
} else {
|
||||||
|
Err(QuoteError::TdxAttGetQuote {
|
||||||
|
msg: "tdx_att_get_quote: No quote returned".into(),
|
||||||
|
inner: error,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(error.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detect the TEE environment
|
||||||
|
fn detect_tee_environment() -> Result<TEEType, QuoteError> {
|
||||||
|
if std::fs::metadata("/dev/attestation").is_ok() {
|
||||||
|
Ok(TEEType::SGX)
|
||||||
|
} else if std::fs::metadata("/dev/tdx_guest").is_ok() {
|
||||||
|
Ok(TEEType::TDX)
|
||||||
|
} else {
|
||||||
|
Err(QuoteError::UnknownTee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the attestation quote from a TEE
|
||||||
|
pub(crate) fn get_quote(report_data: &[u8]) -> Result<(TEEType, Box<[u8]>), QuoteError> {
|
||||||
|
let report_data_fixed = prepare_report_data(report_data)?;
|
||||||
|
|
||||||
|
match detect_tee_environment()? {
|
||||||
|
TEEType::SGX => Ok((TEEType::SGX, sgx_gramine_get_quote(&report_data_fixed)?)),
|
||||||
|
TEEType::TDX => Ok((TEEType::TDX, tdx_get_quote(&report_data_fixed)?)),
|
||||||
|
_ => Err(QuoteError::UnknownTee), // For future TEE types
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,27 +10,25 @@ pub mod attestation;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod tcblevel;
|
pub mod tcblevel;
|
||||||
|
|
||||||
use crate::{
|
#[cfg_attr(all(target_os = "linux", target_arch = "x86_64"), path = "intel.rs")]
|
||||||
quote::error::{QuoteContext as _, QuoteError},
|
#[cfg_attr(
|
||||||
sgx::sgx_gramine_get_quote,
|
not(all(target_os = "linux", target_arch = "x86_64")),
|
||||||
tdx::tgx_get_quote,
|
path = "phala.rs"
|
||||||
};
|
)]
|
||||||
use bytemuck::{cast_slice, AnyBitPattern};
|
mod os;
|
||||||
use intel_tee_quote_verification_rs::{
|
|
||||||
sgx_ql_qv_result_t, sgx_ql_qv_supplemental_t, tee_get_supplemental_data_version_and_size,
|
use crate::quote::{
|
||||||
tee_supp_data_descriptor_t, tee_verify_quote, Collateral,
|
error::{QuoteContext as _, QuoteError},
|
||||||
|
tcblevel::TcbLevel,
|
||||||
};
|
};
|
||||||
|
use bytemuck::AnyBitPattern;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
ffi::CStr,
|
|
||||||
fmt::{Display, Formatter},
|
fmt::{Display, Formatter},
|
||||||
io::Read,
|
io::Read,
|
||||||
mem,
|
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
use tracing::{trace, warn};
|
use tracing::trace;
|
||||||
|
|
||||||
pub use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub const TEE_TYPE_SGX: u32 = 0x00000000;
|
pub const TEE_TYPE_SGX: u32 = 0x00000000;
|
||||||
|
@ -603,35 +601,13 @@ impl FromStr for TEEType {
|
||||||
|
|
||||||
/// Get the attestation quote from a TEE
|
/// Get the attestation quote from a TEE
|
||||||
pub fn get_quote(report_data: &[u8]) -> Result<(TEEType, Box<[u8]>), QuoteError> {
|
pub fn get_quote(report_data: &[u8]) -> Result<(TEEType, Box<[u8]>), QuoteError> {
|
||||||
// check, if we are running in a TEE
|
os::get_quote(report_data)
|
||||||
if std::fs::metadata("/dev/attestation").is_ok() {
|
|
||||||
if report_data.len() > 64 {
|
|
||||||
return Err(QuoteError::ReportDataSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut report_data_fixed = [0u8; 64];
|
|
||||||
report_data_fixed[..report_data.len()].copy_from_slice(report_data);
|
|
||||||
|
|
||||||
Ok((TEEType::SGX, sgx_gramine_get_quote(&report_data_fixed)?))
|
|
||||||
} else if std::fs::metadata("/dev/tdx_guest").is_ok() {
|
|
||||||
if report_data.len() > 64 {
|
|
||||||
return Err(QuoteError::ReportDataSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut report_data_fixed = [0u8; 64];
|
|
||||||
report_data_fixed[..report_data.len()].copy_from_slice(report_data);
|
|
||||||
|
|
||||||
Ok((TEEType::TDX, tgx_get_quote(&report_data_fixed)?))
|
|
||||||
} else {
|
|
||||||
// if not, return an error
|
|
||||||
Err(QuoteError::UnknownTee)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of the quote verification
|
/// The result of the quote verification
|
||||||
pub struct QuoteVerificationResult {
|
pub struct QuoteVerificationResult {
|
||||||
/// the raw result
|
/// the raw result
|
||||||
pub result: sgx_ql_qv_result_t,
|
pub result: TcbLevel,
|
||||||
/// indicates if the collateral is expired
|
/// indicates if the collateral is expired
|
||||||
pub collateral_expired: bool,
|
pub collateral_expired: bool,
|
||||||
/// the earliest expiration date of the collateral
|
/// the earliest expiration date of the collateral
|
||||||
|
@ -644,93 +620,41 @@ pub struct QuoteVerificationResult {
|
||||||
pub quote: Quote,
|
pub quote: Quote,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The collateral data needed to do remote attestation for SGX and TDX
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Collateral {
|
||||||
|
/// Major version of the collateral data
|
||||||
|
pub major_version: u16,
|
||||||
|
/// Minor version of the collateral data
|
||||||
|
pub minor_version: u16,
|
||||||
|
/// Type of TEE (SGX=0, TDX=0x81)
|
||||||
|
pub tee_type: u32,
|
||||||
|
/// The PCK CRL issuer chain used for validating the PCK CRL
|
||||||
|
pub pck_crl_issuer_chain: Box<[u8]>,
|
||||||
|
/// The root CA CRL used for validating the PCK CRL issuer chain
|
||||||
|
pub root_ca_crl: Box<[u8]>,
|
||||||
|
/// The PCK CRL used for validating the PCK certificate
|
||||||
|
pub pck_crl: Box<[u8]>,
|
||||||
|
/// The TCB info issuer chain used for validating the TCB info
|
||||||
|
pub tcb_info_issuer_chain: Box<[u8]>,
|
||||||
|
/// The TCB info used for determining the TCB level
|
||||||
|
pub tcb_info: Box<[u8]>,
|
||||||
|
/// The QE identity issuer chain used for validating the QE identity
|
||||||
|
pub qe_identity_issuer_chain: Box<[u8]>,
|
||||||
|
/// The QE identity used for validating the QE
|
||||||
|
pub qe_identity: Box<[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the collateral data from an SGX or TDX quote
|
||||||
|
pub fn get_collateral(quote: &[u8]) -> Result<Collateral, QuoteError> {
|
||||||
|
os::get_collateral(quote)
|
||||||
|
}
|
||||||
|
|
||||||
/// Verifies a quote with optional collateral material
|
/// Verifies a quote with optional collateral material
|
||||||
pub fn verify_quote_with_collateral(
|
pub fn verify_quote_with_collateral(
|
||||||
quote: &[u8],
|
quote: &[u8],
|
||||||
collateral: Option<&Collateral>,
|
collateral: Option<&Collateral>,
|
||||||
current_time: i64,
|
current_time: i64,
|
||||||
) -> Result<QuoteVerificationResult, QuoteError> {
|
) -> Result<QuoteVerificationResult, QuoteError> {
|
||||||
let mut supp_data: mem::MaybeUninit<sgx_ql_qv_supplemental_t> = mem::MaybeUninit::zeroed();
|
os::verify_quote_with_collateral(quote, collateral, current_time)
|
||||||
let mut supp_data_desc = tee_supp_data_descriptor_t {
|
|
||||||
major_version: 0,
|
|
||||||
data_size: 0,
|
|
||||||
p_data: supp_data.as_mut_ptr() as *mut u8,
|
|
||||||
};
|
|
||||||
trace!("tee_get_supplemental_data_version_and_size");
|
|
||||||
let (_, supp_size) =
|
|
||||||
tee_get_supplemental_data_version_and_size(quote).map_err(|e| QuoteError::Quote3Error {
|
|
||||||
msg: "tee_get_supplemental_data_version_and_size".into(),
|
|
||||||
inner: e,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
trace!(
|
|
||||||
"tee_get_supplemental_data_version_and_size supp_size: {}",
|
|
||||||
supp_size
|
|
||||||
);
|
|
||||||
|
|
||||||
if supp_size == mem::size_of::<sgx_ql_qv_supplemental_t>() as u32 {
|
|
||||||
supp_data_desc.data_size = supp_size;
|
|
||||||
} else {
|
|
||||||
supp_data_desc.data_size = 0;
|
|
||||||
trace!(
|
|
||||||
"tee_get_supplemental_data_version_and_size supp_size: {}",
|
|
||||||
supp_size
|
|
||||||
);
|
|
||||||
trace!(
|
|
||||||
"mem::size_of::<sgx_ql_qv_supplemental_t>(): {}",
|
|
||||||
mem::size_of::<sgx_ql_qv_supplemental_t>()
|
|
||||||
);
|
|
||||||
warn!("Quote supplemental data size is different between DCAP QVL and QvE, please make sure you installed DCAP QVL and QvE from same release.")
|
|
||||||
}
|
|
||||||
|
|
||||||
let p_supplemental_data = match supp_data_desc.data_size {
|
|
||||||
0 => None,
|
|
||||||
_ => Some(&mut supp_data_desc),
|
|
||||||
};
|
|
||||||
|
|
||||||
let has_sup = p_supplemental_data.is_some();
|
|
||||||
|
|
||||||
trace!("tee_verify_quote");
|
|
||||||
|
|
||||||
let (collateral_expiration_status, result) =
|
|
||||||
tee_verify_quote(quote, collateral, current_time, None, p_supplemental_data)
|
|
||||||
.context("tee_verify_quote")?;
|
|
||||||
|
|
||||||
trace!("tee_verify_quote end");
|
|
||||||
|
|
||||||
// check supplemental data if necessary
|
|
||||||
let (advisories, earliest_expiration_date, tcb_level_date_tag) = if has_sup {
|
|
||||||
unsafe {
|
|
||||||
let supp_data = supp_data.assume_init();
|
|
||||||
// convert to valid UTF-8 string
|
|
||||||
let ads = CStr::from_bytes_until_nul(cast_slice(&supp_data.sa_list[..]))
|
|
||||||
.ok()
|
|
||||||
.and_then(|s| CStr::to_str(s).ok())
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|s| s.split(',').map(str::trim).map(String::from))
|
|
||||||
.filter(|s| !s.is_empty())
|
|
||||||
.collect();
|
|
||||||
(
|
|
||||||
ads,
|
|
||||||
supp_data.earliest_expiration_date,
|
|
||||||
supp_data.tcb_level_date_tag,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(vec![], 0, 0)
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!("Quote::parse");
|
|
||||||
let quote = Quote::parse(quote)?;
|
|
||||||
|
|
||||||
let res = QuoteVerificationResult {
|
|
||||||
collateral_expired: collateral_expiration_status != 0,
|
|
||||||
earliest_expiration_date,
|
|
||||||
tcb_level_date_tag,
|
|
||||||
result,
|
|
||||||
quote,
|
|
||||||
advisories,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
}
|
||||||
|
|
259
crates/teepot/src/quote/phala.rs
Normal file
259
crates/teepot/src/quote/phala.rs
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2023-2025 Matter Labs
|
||||||
|
|
||||||
|
//! Emulate Intel DCAP library collateral and verification
|
||||||
|
|
||||||
|
use crate::quote::{
|
||||||
|
error::{QuoteContext, QuoteContextErr, QuoteError},
|
||||||
|
tcblevel::TcbLevel,
|
||||||
|
Collateral, Quote, QuoteVerificationResult, TEEType,
|
||||||
|
};
|
||||||
|
use bytes::Bytes;
|
||||||
|
use dcap_qvl::{verify::VerifiedReport, QuoteCollateralV3};
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// Helper function to extract a required header value from a response
|
||||||
|
fn extract_header_value(
|
||||||
|
response: &reqwest::Response,
|
||||||
|
header_name: &str,
|
||||||
|
) -> Result<String, QuoteError> {
|
||||||
|
response
|
||||||
|
.headers()
|
||||||
|
.get(header_name)
|
||||||
|
.ok_or_else(|| QuoteError::Unexpected(format!("Missing required header: {header_name}")))?
|
||||||
|
.to_str()
|
||||||
|
.map_err(|e| QuoteError::Unexpected(format!("Invalid header value: {e}")))
|
||||||
|
.map(|val| val.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch collateral data from Intel's Provisioning Certification Service
|
||||||
|
async fn fetch_pcs_collateral(
|
||||||
|
quote: &[u8],
|
||||||
|
) -> Result<(QuoteCollateralV3, String, Bytes), QuoteError> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let response = client
|
||||||
|
.get("https://api.trustedservices.intel.com/sgx/certification/v4/pckcrl?ca=platform")
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| QuoteError::Unexpected(format!("Failed to fetch collateral: {e}")))?;
|
||||||
|
|
||||||
|
// Extract required fields
|
||||||
|
let issuer_chain = extract_header_value(&response, "SGX-PCK-CRL-Issuer-Chain")?;
|
||||||
|
let pck_crl_data = response
|
||||||
|
.bytes()
|
||||||
|
.await
|
||||||
|
.map_err(|e| QuoteError::Unexpected(format!("Failed to fetch collateral data: {e}")))?;
|
||||||
|
|
||||||
|
// Fetch the full collateral
|
||||||
|
dcap_qvl::collateral::get_collateral_from_pcs(quote, std::time::Duration::from_secs(1000))
|
||||||
|
.await
|
||||||
|
.map(|collateral| (collateral, issuer_chain, pck_crl_data))
|
||||||
|
.str_context("Fetching PCS collateral with `get_collateral_from_pcs`")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get collateral for a quote, handling the async operations
|
||||||
|
pub(crate) fn get_collateral(quote: &[u8]) -> Result<Collateral, QuoteError> {
|
||||||
|
// Execute the async operation in a separate thread
|
||||||
|
let result = std::thread::scope(|s| {
|
||||||
|
s.spawn(|| {
|
||||||
|
// Create a minimal runtime for this thread only
|
||||||
|
let rt = tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.context("Failed to build tokio runtime")?;
|
||||||
|
|
||||||
|
// Run the async function
|
||||||
|
rt.block_on(fetch_pcs_collateral(quote))
|
||||||
|
})
|
||||||
|
.join()
|
||||||
|
.map_err(|_| QuoteError::Unexpected("Thread panic in get_collateral".into()))
|
||||||
|
})??;
|
||||||
|
|
||||||
|
// Destructure the result
|
||||||
|
let (collateral, pck_crl, pck_issuer_chain) = result;
|
||||||
|
|
||||||
|
// Convert QuoteCollateralV3 to Collateral
|
||||||
|
convert_to_collateral(collateral, pck_crl, pck_issuer_chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to convert QuoteCollateralV3 to Collateral
|
||||||
|
fn convert_to_collateral(
|
||||||
|
collateral: QuoteCollateralV3,
|
||||||
|
pck_crl: String,
|
||||||
|
pck_issuer_chain: Bytes,
|
||||||
|
) -> Result<Collateral, QuoteError> {
|
||||||
|
let QuoteCollateralV3 {
|
||||||
|
tcb_info_issuer_chain,
|
||||||
|
tcb_info,
|
||||||
|
tcb_info_signature,
|
||||||
|
qe_identity_issuer_chain,
|
||||||
|
qe_identity,
|
||||||
|
qe_identity_signature,
|
||||||
|
} = collateral;
|
||||||
|
|
||||||
|
let tcb_info_signature = hex::encode(tcb_info_signature);
|
||||||
|
let qe_identity_signature = hex::encode(qe_identity_signature);
|
||||||
|
|
||||||
|
// Create strings with proper formatting
|
||||||
|
let tcb_info_json =
|
||||||
|
format!("{{ \"tcbInfo\": {tcb_info}, \"signature\": \"{tcb_info_signature}\" }}");
|
||||||
|
|
||||||
|
let qe_identity_json = format!(
|
||||||
|
"{{ \"enclaveIdentity\": {qe_identity}, \"signature\": \"{qe_identity_signature}\" }}"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Helper to create CString and convert to Box<[u8]>
|
||||||
|
let to_bytes_with_nul = |s: String, context: &str| -> Result<Box<[u8]>, QuoteError> {
|
||||||
|
Ok(CString::new(s)
|
||||||
|
.str_context(context)?
|
||||||
|
.as_bytes_with_nul()
|
||||||
|
.into())
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Collateral {
|
||||||
|
// Default/unhandled values
|
||||||
|
major_version: 0,
|
||||||
|
minor_version: 0,
|
||||||
|
tee_type: 0,
|
||||||
|
root_ca_crl: Box::new([]),
|
||||||
|
|
||||||
|
// Converted values
|
||||||
|
pck_crl_issuer_chain: pck_issuer_chain.as_ref().into(),
|
||||||
|
pck_crl: pck_crl.as_bytes().into(),
|
||||||
|
tcb_info_issuer_chain: to_bytes_with_nul(tcb_info_issuer_chain, "tcb_info_issuer_chain")?,
|
||||||
|
tcb_info: to_bytes_with_nul(tcb_info_json, "tcb_info")?,
|
||||||
|
qe_identity_issuer_chain: to_bytes_with_nul(
|
||||||
|
qe_identity_issuer_chain,
|
||||||
|
"qe_identity_issuer_chain",
|
||||||
|
)?,
|
||||||
|
qe_identity: to_bytes_with_nul(qe_identity_json, "qe_identity")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split the last zero byte
|
||||||
|
fn get_str_from_bytes(bytes: &[u8], context: &str) -> Result<String, QuoteError> {
|
||||||
|
let c_str = CStr::from_bytes_until_nul(bytes)
|
||||||
|
.str_context(format!("Failed to extract CString: {}", context))?;
|
||||||
|
Ok(c_str.to_string_lossy().into_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse JSON field from collateral data
|
||||||
|
fn parse_json_field(data: &[u8], context: &str) -> Result<serde_json::Value, QuoteError> {
|
||||||
|
serde_json::from_str(&get_str_from_bytes(data, context)?)
|
||||||
|
.str_context(format!("Failed to parse JSON: {}", context))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert Collateral to QuoteCollateralV3
|
||||||
|
fn convert_collateral(collateral: &Collateral) -> Result<QuoteCollateralV3, QuoteError> {
|
||||||
|
// Parse TCB info
|
||||||
|
let tcb_info_json = parse_json_field(collateral.tcb_info.as_ref(), "tcb_info_json")?;
|
||||||
|
|
||||||
|
let tcb_info = tcb_info_json["tcbInfo"].to_string();
|
||||||
|
let tcb_info_signature = tcb_info_json
|
||||||
|
.get("signature")
|
||||||
|
.context("TCB Info missing 'signature' field")?
|
||||||
|
.as_str()
|
||||||
|
.context("TCB Info signature must be a string")?;
|
||||||
|
|
||||||
|
let tcb_info_signature = hex::decode(tcb_info_signature)
|
||||||
|
.ok()
|
||||||
|
.context("TCB Info signature must be valid hex")?;
|
||||||
|
|
||||||
|
// Parse QE identity
|
||||||
|
let qe_identity_json = parse_json_field(collateral.qe_identity.as_ref(), "qe_identity_json")?;
|
||||||
|
|
||||||
|
let qe_identity = qe_identity_json
|
||||||
|
.get("enclaveIdentity")
|
||||||
|
.context("QE Identity missing 'enclaveIdentity' field")?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let qe_identity_signature = qe_identity_json
|
||||||
|
.get("signature")
|
||||||
|
.context("QE Identity missing 'signature' field")?
|
||||||
|
.as_str()
|
||||||
|
.context("QE Identity signature must be a string")?;
|
||||||
|
|
||||||
|
let qe_identity_signature = hex::decode(qe_identity_signature)
|
||||||
|
.ok()
|
||||||
|
.context("QE Identity signature must be valid hex")?;
|
||||||
|
|
||||||
|
Ok(QuoteCollateralV3 {
|
||||||
|
tcb_info_issuer_chain: get_str_from_bytes(
|
||||||
|
collateral.tcb_info_issuer_chain.as_ref(),
|
||||||
|
"convert_collateral: tcb_info_issuer_chain",
|
||||||
|
)?,
|
||||||
|
tcb_info,
|
||||||
|
tcb_info_signature,
|
||||||
|
qe_identity_issuer_chain: get_str_from_bytes(
|
||||||
|
collateral.qe_identity_issuer_chain.as_ref(),
|
||||||
|
"convert_collateral: qe_identity_issuer_chain",
|
||||||
|
)?,
|
||||||
|
qe_identity,
|
||||||
|
qe_identity_signature,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify a quote with the provided collateral
|
||||||
|
pub(crate) fn verify_quote_with_collateral(
|
||||||
|
quote: &[u8],
|
||||||
|
collateral: Option<&Collateral>,
|
||||||
|
current_time: i64,
|
||||||
|
) -> Result<QuoteVerificationResult, QuoteError> {
|
||||||
|
// Convert collateral or return error if not provided
|
||||||
|
let collateral = collateral
|
||||||
|
.ok_or_else(|| QuoteError::Unexpected("No collateral provided".into()))
|
||||||
|
.and_then(convert_collateral)?;
|
||||||
|
|
||||||
|
// Convert current time to u64
|
||||||
|
let current_time_u64 = current_time
|
||||||
|
.try_into()
|
||||||
|
.str_context("Failed to convert current_time to u64")?;
|
||||||
|
|
||||||
|
// Verify the quote
|
||||||
|
let verified_report = dcap_qvl::verify::verify(quote, &collateral, current_time_u64)
|
||||||
|
.expect("Failed to verify quote");
|
||||||
|
|
||||||
|
let VerifiedReport {
|
||||||
|
status,
|
||||||
|
advisory_ids,
|
||||||
|
report: _,
|
||||||
|
} = verified_report;
|
||||||
|
|
||||||
|
// Parse TCB level from status
|
||||||
|
let result =
|
||||||
|
TcbLevel::from_str(&status).str_context("Failed to parse TCB level from status")?;
|
||||||
|
|
||||||
|
// Parse quote
|
||||||
|
let quote = Quote::parse(quote)?;
|
||||||
|
|
||||||
|
let tcb_info_json: serde_json::Value =
|
||||||
|
serde_json::from_str(&String::from_utf8_lossy(collateral.tcb_info.as_ref()))
|
||||||
|
.str_context("verify_quote_with_collateral tcb_info_json")?;
|
||||||
|
|
||||||
|
let next_update = tcb_info_json
|
||||||
|
.get("nextUpdate")
|
||||||
|
.context("verify_quote_with_collateral: TCB Info missing 'nextUpdate' field")?
|
||||||
|
.as_str()
|
||||||
|
.context("verify_quote_with_collateral: TCB Info nextUpdate must be a string")?;
|
||||||
|
|
||||||
|
let next_update = chrono::DateTime::parse_from_rfc3339(next_update)
|
||||||
|
.ok()
|
||||||
|
.context("verify_quote_with_collateral: Failed to parse next update")?;
|
||||||
|
|
||||||
|
Ok(QuoteVerificationResult {
|
||||||
|
result,
|
||||||
|
collateral_expired: result == TcbLevel::OutOfDate,
|
||||||
|
earliest_expiration_date: next_update
|
||||||
|
.signed_duration_since(chrono::DateTime::UNIX_EPOCH)
|
||||||
|
.num_seconds(),
|
||||||
|
tcb_level_date_tag: 0,
|
||||||
|
advisories: advisory_ids,
|
||||||
|
quote,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the attestation quote from a TEE
|
||||||
|
pub fn get_quote(_report_data: &[u8]) -> Result<(TEEType, Box<[u8]>), QuoteError> {
|
||||||
|
Err(QuoteError::UnknownTee)
|
||||||
|
}
|
|
@ -4,7 +4,6 @@
|
||||||
//! Intel SGX Enclave TCB level wrapper
|
//! Intel SGX Enclave TCB level wrapper
|
||||||
|
|
||||||
use enumset::EnumSetType;
|
use enumset::EnumSetType;
|
||||||
use intel_tee_quote_verification_rs::sgx_ql_qv_result_t;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{Display, Formatter},
|
fmt::{Display, Formatter},
|
||||||
|
@ -34,37 +33,20 @@ pub enum TcbLevel {
|
||||||
Invalid,
|
Invalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<sgx_ql_qv_result_t> for TcbLevel {
|
|
||||||
fn from(value: sgx_ql_qv_result_t) -> Self {
|
|
||||||
match value {
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => TcbLevel::Ok,
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE => TcbLevel::OutOfDate,
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED => {
|
|
||||||
TcbLevel::OutOfDateConfigNeeded
|
|
||||||
}
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED => TcbLevel::SwHardeningNeeded,
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => {
|
|
||||||
TcbLevel::ConfigAndSwHardeningNeeded
|
|
||||||
}
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_NEEDED => TcbLevel::ConfigNeeded,
|
|
||||||
_ => TcbLevel::Invalid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for TcbLevel {
|
impl FromStr for TcbLevel {
|
||||||
type Err = ();
|
type Err = String;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s.to_ascii_lowercase().as_str() {
|
||||||
"Ok" => Ok(TcbLevel::Ok),
|
"ok" => Ok(TcbLevel::Ok),
|
||||||
"ConfigNeeded" => Ok(TcbLevel::ConfigNeeded),
|
"uptodate" => Ok(TcbLevel::Ok),
|
||||||
"ConfigAndSwHardeningNeeded" => Ok(TcbLevel::ConfigAndSwHardeningNeeded),
|
"configneeded" => Ok(TcbLevel::ConfigNeeded),
|
||||||
"SwHardeningNeeded" => Ok(TcbLevel::SwHardeningNeeded),
|
"configandswhardeningneeded" => Ok(TcbLevel::ConfigAndSwHardeningNeeded),
|
||||||
"OutOfDate" => Ok(TcbLevel::OutOfDate),
|
"swhardeningneeded" => Ok(TcbLevel::SwHardeningNeeded),
|
||||||
"OutOfDateConfigNeeded" => Ok(TcbLevel::OutOfDateConfigNeeded),
|
"outofdate" => Ok(TcbLevel::OutOfDate),
|
||||||
"Invalid" => Ok(TcbLevel::Invalid),
|
"outofdateconfigneeded" => Ok(TcbLevel::OutOfDateConfigNeeded),
|
||||||
_ => Err(()),
|
"invalid" => Ok(TcbLevel::Invalid),
|
||||||
|
_ => Err(format!("Invalid TCB level: {}", s)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,8 @@
|
||||||
pub mod sign;
|
pub mod sign;
|
||||||
|
|
||||||
use crate::quote::error::QuoteContext;
|
use crate::quote::error::QuoteContext;
|
||||||
pub use crate::quote::error::QuoteError;
|
pub use crate::quote::{error::QuoteError, Collateral};
|
||||||
use bytemuck::{try_from_bytes, AnyBitPattern, PodCastError};
|
use bytemuck::{try_from_bytes, AnyBitPattern, PodCastError};
|
||||||
pub use intel_tee_quote_verification_rs::{sgx_ql_qv_result_t, Collateral};
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::OpenOptions,
|
fs::OpenOptions,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
|
|
|
@ -3,29 +3,17 @@
|
||||||
|
|
||||||
//! Intel TDX helper functions.
|
//! Intel TDX helper functions.
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
pub mod rtmr;
|
pub mod rtmr;
|
||||||
|
|
||||||
use crate::sgx::QuoteError;
|
/// The sha384 digest of 0u32, which is used in the UEFI TPM protocol
|
||||||
pub use intel_tee_quote_verification_rs::Collateral;
|
/// as a marker. Used to advance the PCR.
|
||||||
use tdx_attest_rs::{tdx_att_get_quote, tdx_attest_error_t, tdx_report_data_t};
|
/// ```shell
|
||||||
|
/// $ echo -n -e "\000\000\000\000" | sha384sum -b
|
||||||
/// Get a TDX quote
|
/// 394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0 *-
|
||||||
pub fn tgx_get_quote(report_data_bytes: &[u8; 64]) -> Result<Box<[u8]>, QuoteError> {
|
/// ```
|
||||||
let mut tdx_report_data = tdx_report_data_t { d: [0; 64usize] };
|
pub const UEFI_MARKER_DIGEST_BYTES: [u8; 48] = [
|
||||||
tdx_report_data.d.copy_from_slice(report_data_bytes);
|
0x39, 0x43, 0x41, 0xb7, 0x18, 0x2c, 0xd2, 0x27, 0xc5, 0xc6, 0xb0, 0x7e, 0xf8, 0x00, 0x0c, 0xdf,
|
||||||
|
0xd8, 0x61, 0x36, 0xc4, 0x29, 0x2b, 0x8e, 0x57, 0x65, 0x73, 0xad, 0x7e, 0xd9, 0xae, 0x41, 0x01,
|
||||||
let (error, quote) = tdx_att_get_quote(Some(&tdx_report_data), None, None, 0);
|
0x9f, 0x58, 0x18, 0xb4, 0xb9, 0x71, 0xc9, 0xef, 0xfc, 0x60, 0xe1, 0xad, 0x9f, 0x12, 0x89, 0xf0,
|
||||||
|
];
|
||||||
if error == tdx_attest_error_t::TDX_ATTEST_SUCCESS {
|
|
||||||
if let Some(quote) = quote {
|
|
||||||
Ok(quote.into())
|
|
||||||
} else {
|
|
||||||
Err(QuoteError::TdxAttGetQuote {
|
|
||||||
msg: "tdx_att_get_quote: No quote returned".into(),
|
|
||||||
inner: error,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(error.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,18 +5,6 @@
|
||||||
|
|
||||||
use crate::sgx::QuoteError;
|
use crate::sgx::QuoteError;
|
||||||
|
|
||||||
/// The sha384 digest of 0u32, which is used in the UEFI TPM protocol
|
|
||||||
/// as a marker. Used to advance the PCR.
|
|
||||||
/// ```shell
|
|
||||||
/// $ echo -n -e "\000\000\000\000" | sha384sum -b
|
|
||||||
/// 394341b7182cd227c5c6b07ef8000cdfd86136c4292b8e576573ad7ed9ae41019f5818b4b971c9effc60e1ad9f1289f0 *-
|
|
||||||
/// ```
|
|
||||||
pub const UEFI_MARKER_DIGEST_BYTES: [u8; 48] = [
|
|
||||||
0x39, 0x43, 0x41, 0xb7, 0x18, 0x2c, 0xd2, 0x27, 0xc5, 0xc6, 0xb0, 0x7e, 0xf8, 0x00, 0x0c, 0xdf,
|
|
||||||
0xd8, 0x61, 0x36, 0xc4, 0x29, 0x2b, 0x8e, 0x57, 0x65, 0x73, 0xad, 0x7e, 0xd9, 0xae, 0x41, 0x01,
|
|
||||||
0x9f, 0x58, 0x18, 0xb4, 0xb9, 0x71, 0xc9, 0xef, 0xfc, 0x60, 0xe1, 0xad, 0x9f, 0x12, 0x89, 0xf0,
|
|
||||||
];
|
|
||||||
|
|
||||||
/// The actual rtmr event data handled in DCAP
|
/// The actual rtmr event data handled in DCAP
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub struct TdxRtmrEvent {
|
pub struct TdxRtmrEvent {
|
||||||
|
@ -103,7 +91,7 @@ impl From<TdxRtmrEvent> for Vec<u8> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::UEFI_MARKER_DIGEST_BYTES;
|
use crate::tdx::UEFI_MARKER_DIGEST_BYTES;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uefi_marker_digest() {
|
fn test_uefi_marker_digest() {
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (c) 2024 Matter Labs
|
// Copyright (c) 2024-2025 Matter Labs
|
||||||
|
|
||||||
mod sgx {
|
mod sgx {
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use intel_tee_quote_verification_rs::{sgx_ql_qv_result_t, Collateral};
|
|
||||||
use std::time::{Duration, UNIX_EPOCH};
|
use std::time::{Duration, UNIX_EPOCH};
|
||||||
use teepot::{
|
use teepot::{
|
||||||
prover::reportdata::{ReportData, ReportDataV1},
|
prover::reportdata::{ReportData, ReportDataV1},
|
||||||
quote::{verify_quote_with_collateral, Quote, QuoteVerificationResult, Report},
|
quote::{
|
||||||
|
tcblevel::TcbLevel, verify_quote_with_collateral, Collateral, Quote,
|
||||||
|
QuoteVerificationResult, Report,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use tracing_test::traced_test;
|
use tracing_test::traced_test;
|
||||||
|
|
||||||
|
@ -17,7 +19,7 @@ mod sgx {
|
||||||
current_time: i64,
|
current_time: i64,
|
||||||
expected_mrsigner: &[u8],
|
expected_mrsigner: &[u8],
|
||||||
expected_reportdata: &[u8],
|
expected_reportdata: &[u8],
|
||||||
expected_result: sgx_ql_qv_result_t,
|
expected_result: TcbLevel,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let QuoteVerificationResult {
|
let QuoteVerificationResult {
|
||||||
collateral_expired,
|
collateral_expired,
|
||||||
|
@ -28,7 +30,7 @@ mod sgx {
|
||||||
tcb_level_date_tag,
|
tcb_level_date_tag,
|
||||||
} = verify_quote_with_collateral(quote, collateral, current_time)?;
|
} = verify_quote_with_collateral(quote, collateral, current_time)?;
|
||||||
|
|
||||||
if collateral_expired || result != sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK {
|
if collateral_expired || result != TcbLevel::Ok {
|
||||||
print!("Attestation failed: ");
|
print!("Attestation failed: ");
|
||||||
|
|
||||||
if collateral_expired {
|
if collateral_expired {
|
||||||
|
@ -40,28 +42,7 @@ mod sgx {
|
||||||
earliest_expiration_date
|
earliest_expiration_date
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
println!("{result:?}");
|
||||||
match result {
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => (),
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_NEEDED => println!("Config needed"),
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => {
|
|
||||||
println!("Config and Software hardening needed")
|
|
||||||
}
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE => println!("Out of date"),
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED => {
|
|
||||||
println!("Out of Date and Config needed")
|
|
||||||
}
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED => {
|
|
||||||
println!("Software hardening needed")
|
|
||||||
}
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_INVALID_SIGNATURE => {
|
|
||||||
println!("Invalid signature")
|
|
||||||
}
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_REVOKED => println!("Revoked"),
|
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_UNSPECIFIED => println!("Unspecified"),
|
|
||||||
|
|
||||||
_ => println!("Unknown state!"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for advisory in advisories {
|
for advisory in advisories {
|
||||||
|
@ -85,6 +66,8 @@ mod sgx {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
// alternative quote verification cannot cope with old collateral data format
|
||||||
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
fn sw_hardening() {
|
fn sw_hardening() {
|
||||||
let quote = [
|
let quote = [
|
||||||
0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x93, 0x9a,
|
0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x93, 0x9a,
|
||||||
|
@ -1144,12 +1127,14 @@ mod sgx {
|
||||||
current_time,
|
current_time,
|
||||||
&mrsigner,
|
&mrsigner,
|
||||||
&report_data,
|
&report_data,
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED,
|
TcbLevel::SwHardeningNeeded,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
// alternative quote verification cannot cope with old collateral data format
|
||||||
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
fn out_of_date() {
|
fn out_of_date() {
|
||||||
let quote = [
|
let quote = [
|
||||||
0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x93, 0x9a,
|
0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x93, 0x9a,
|
||||||
|
@ -2215,7 +2200,7 @@ mod sgx {
|
||||||
current_time,
|
current_time,
|
||||||
&mrsigner,
|
&mrsigner,
|
||||||
&report_data,
|
&report_data,
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE,
|
TcbLevel::OutOfDate,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -2598,12 +2583,14 @@ mod sgx {
|
||||||
current_time,
|
current_time,
|
||||||
&mrsigner,
|
&mrsigner,
|
||||||
&report_data,
|
&report_data,
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED,
|
TcbLevel::SwHardeningNeeded,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
|
// alternative quote verification errors on debug flag
|
||||||
fn vault_proxy() {
|
fn vault_proxy() {
|
||||||
let quote = [
|
let quote = [
|
||||||
3, 0, 2, 0, 0, 0, 0, 0, 9, 0, 14, 0, 147, 154, 114, 51, 247, 156, 76, 169, 148, 10, 13,
|
3, 0, 2, 0, 0, 0, 0, 0, 9, 0, 14, 0, 147, 154, 114, 51, 247, 156, 76, 169, 148, 10, 13,
|
||||||
|
@ -3681,7 +3668,7 @@ mod sgx {
|
||||||
current_time,
|
current_time,
|
||||||
&mrsigner,
|
&mrsigner,
|
||||||
&report_data,
|
&report_data,
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED,
|
TcbLevel::SwHardeningNeeded,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -4809,7 +4796,7 @@ mod sgx {
|
||||||
current_time as i64,
|
current_time as i64,
|
||||||
&mrsigner,
|
&mrsigner,
|
||||||
&report_data,
|
&report_data,
|
||||||
sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK,
|
TcbLevel::Ok,
|
||||||
)
|
)
|
||||||
.context("check_quote")?;
|
.context("check_quote")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -32,6 +32,7 @@ allow = [
|
||||||
"BSD-3-Clause",
|
"BSD-3-Clause",
|
||||||
"OpenSSL",
|
"OpenSSL",
|
||||||
"CC0-1.0",
|
"CC0-1.0",
|
||||||
|
"Zlib",
|
||||||
]
|
]
|
||||||
confidence-threshold = 0.8
|
confidence-threshold = 0.8
|
||||||
exceptions = []
|
exceptions = []
|
||||||
|
|
6
flake.lock
generated
6
flake.lock
generated
|
@ -234,11 +234,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1734661750,
|
"lastModified": 1743993291,
|
||||||
"narHash": "sha256-BI58NBdimxu1lnpOrG9XxBz7Cwqy+qIf99zunWofX5w=",
|
"narHash": "sha256-u8GHvduU1gCtoFXvTS/wGjH1ouv5S/GRGq6MAT+sG/k=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "7d3d910d5fd575e6e8c5600d83d54e5c47273bfe",
|
"rev": "0cb3c8979c65dc6a5812dfe67499a8c7b8b4325b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ teepot
|
{ teepot
|
||||||
, pkgs
|
, pkgs
|
||||||
|
, stdenv
|
||||||
, container-name ? "teepot-self-attestation-test-sgx-azure"
|
, container-name ? "teepot-self-attestation-test-sgx-azure"
|
||||||
, tag ? null
|
, tag ? null
|
||||||
, isAzure ? true
|
, isAzure ? true
|
||||||
}:
|
}:
|
||||||
|
if (stdenv.hostPlatform.system != "x86_64-linux") then { } else
|
||||||
pkgs.lib.tee.sgxGramineContainer {
|
pkgs.lib.tee.sgxGramineContainer {
|
||||||
name = container-name;
|
name = container-name;
|
||||||
inherit tag;
|
inherit tag;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ lib
|
{ lib
|
||||||
|
, stdenv
|
||||||
, openssl
|
, openssl
|
||||||
, curl
|
, curl
|
||||||
, dockerTools
|
, dockerTools
|
||||||
|
@ -8,28 +9,31 @@
|
||||||
, teepot
|
, teepot
|
||||||
, nixsgx
|
, nixsgx
|
||||||
}:
|
}:
|
||||||
dockerTools.buildLayeredImage {
|
if (stdenv.hostPlatform.isDarwin) then {
|
||||||
name = "tdx-test";
|
# FIXME: dockerTools.buildLayeredImage seems to be broken on Darwin
|
||||||
|
} else
|
||||||
|
dockerTools.buildLayeredImage {
|
||||||
|
name = "tdx-test";
|
||||||
|
|
||||||
config.Entrypoint = [ "${teepot.teepot.tdx_test}/bin/tdx-test-dcap" ];
|
config.Entrypoint = [ "${teepot.teepot.tdx_test}/bin/tdx-test" ];
|
||||||
config.Env = [
|
config.Env = [
|
||||||
"SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt"
|
"SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt"
|
||||||
];
|
|
||||||
|
|
||||||
contents = buildEnv {
|
|
||||||
name = "image-root";
|
|
||||||
|
|
||||||
paths = with dockerTools;[
|
|
||||||
teepot.teepot.tdx_test
|
|
||||||
openssl.out
|
|
||||||
curl.out
|
|
||||||
nixsgx.sgx-dcap.quote_verify
|
|
||||||
nixsgx.sgx-dcap.default_qpl
|
|
||||||
usrBinEnv
|
|
||||||
binSh
|
|
||||||
caCertificates
|
|
||||||
fakeNss
|
|
||||||
];
|
];
|
||||||
pathsToLink = [ "/bin" "/lib" "/etc" "/share" ];
|
|
||||||
};
|
contents = buildEnv {
|
||||||
}
|
name = "image-root";
|
||||||
|
|
||||||
|
paths = with dockerTools; [
|
||||||
|
teepot.teepot.tdx_test
|
||||||
|
openssl.out
|
||||||
|
curl.out
|
||||||
|
# nixsgx.sgx-dcap.quote_verify
|
||||||
|
# nixsgx.sgx-dcap.default_qpl
|
||||||
|
usrBinEnv
|
||||||
|
binSh
|
||||||
|
caCertificates
|
||||||
|
fakeNss
|
||||||
|
];
|
||||||
|
pathsToLink = [ "/bin" "/lib" "/etc" "/share" ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ teepot
|
{ teepot
|
||||||
, pkgs
|
, pkgs
|
||||||
|
, stdenv
|
||||||
, bash
|
, bash
|
||||||
, coreutils
|
, coreutils
|
||||||
, container-name ? "teepot-key-preexec-dcap"
|
, container-name ? "teepot-key-preexec-dcap"
|
||||||
|
@ -9,6 +10,7 @@
|
||||||
}: let
|
}: let
|
||||||
entrypoint = "${bash}/bin/bash";
|
entrypoint = "${bash}/bin/bash";
|
||||||
in
|
in
|
||||||
|
if (stdenv.hostPlatform.system != "x86_64-linux") then { } else
|
||||||
pkgs.lib.tee.sgxGramineContainer {
|
pkgs.lib.tee.sgxGramineContainer {
|
||||||
name = container-name;
|
name = container-name;
|
||||||
inherit tag entrypoint;
|
inherit tag entrypoint;
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ teepot
|
{ teepot
|
||||||
, pkgs
|
, pkgs
|
||||||
|
, stdenv
|
||||||
, container-name ? "teepot-vault-admin-sgx-azure"
|
, container-name ? "teepot-vault-admin-sgx-azure"
|
||||||
, tag ? null
|
, tag ? null
|
||||||
, isAzure ? null
|
, isAzure ? null
|
||||||
}:
|
}:
|
||||||
|
if (stdenv.hostPlatform.system != "x86_64-linux") then { } else
|
||||||
pkgs.lib.tee.sgxGramineContainer {
|
pkgs.lib.tee.sgxGramineContainer {
|
||||||
name = container-name;
|
name = container-name;
|
||||||
inherit tag;
|
inherit tag;
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ dockerTools
|
{ dockerTools
|
||||||
, buildEnv
|
, buildEnv
|
||||||
|
, stdenv
|
||||||
, teepot
|
, teepot
|
||||||
, openssl
|
, openssl
|
||||||
, curl
|
, curl
|
||||||
, nixsgx
|
, nixsgx
|
||||||
}:
|
}:
|
||||||
|
if (stdenv.hostPlatform.system != "x86_64-linux") then { } else
|
||||||
dockerTools.buildLayeredImage {
|
dockerTools.buildLayeredImage {
|
||||||
name = "vault-admin";
|
name = "vault-admin";
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ teepot
|
{ teepot
|
||||||
, pkgs
|
, pkgs
|
||||||
|
, stdenv
|
||||||
, vat
|
, vat
|
||||||
, vault
|
, vault
|
||||||
, container-name ? "teepot-vault-sgx-azure"
|
, container-name ? "teepot-vault-sgx-azure"
|
||||||
|
@ -12,6 +13,7 @@ let
|
||||||
entrypoint = "${teepot.teepot.tee_ratls_preexec}/bin/tee-ratls-preexec";
|
entrypoint = "${teepot.teepot.tee_ratls_preexec}/bin/tee-ratls-preexec";
|
||||||
appDir = "/opt/vault";
|
appDir = "/opt/vault";
|
||||||
in
|
in
|
||||||
|
if (stdenv.hostPlatform.system != "x86_64-linux") then { } else
|
||||||
pkgs.lib.tee.sgxGramineContainer {
|
pkgs.lib.tee.sgxGramineContainer {
|
||||||
name = container-name;
|
name = container-name;
|
||||||
inherit tag;
|
inherit tag;
|
||||||
|
@ -86,5 +88,3 @@ pkgs.lib.tee.sgxGramineContainer {
|
||||||
sys.experimental__enable_flock = true;
|
sys.experimental__enable_flock = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ teepot
|
{ teepot
|
||||||
, pkgs
|
, pkgs
|
||||||
|
, stdenv
|
||||||
, vat
|
, vat
|
||||||
, container-name ? "teepot-vault-unseal-sgx-azure"
|
, container-name ? "teepot-vault-unseal-sgx-azure"
|
||||||
, tag ? null
|
, tag ? null
|
||||||
, isAzure ? true
|
, isAzure ? true
|
||||||
}:
|
}:
|
||||||
|
if (stdenv.hostPlatform.system != "x86_64-linux") then { } else
|
||||||
pkgs.lib.tee.sgxGramineContainer {
|
pkgs.lib.tee.sgxGramineContainer {
|
||||||
name = container-name;
|
name = container-name;
|
||||||
inherit tag isAzure;
|
inherit tag isAzure;
|
||||||
|
|
|
@ -1,30 +1,43 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ dockerTools
|
{ dockerTools
|
||||||
|
, lib
|
||||||
|
, stdenv
|
||||||
, buildEnv
|
, buildEnv
|
||||||
, teepot
|
, teepot
|
||||||
, openssl
|
, openssl
|
||||||
, curl
|
, curl
|
||||||
, nixsgx
|
, nixsgx
|
||||||
}:
|
}:
|
||||||
dockerTools.buildLayeredImage {
|
if (stdenv.hostPlatform.isDarwin) then {
|
||||||
name = "vault-unseal";
|
# FIXME: dockerTools.buildLayeredImage seems to be broken on Darwin
|
||||||
|
} else
|
||||||
|
dockerTools.buildLayeredImage {
|
||||||
|
name = "vault-unseal";
|
||||||
|
|
||||||
config.Entrypoint = [ "${teepot.teepot.vault_unseal}/bin/vault-unseal" ];
|
config.Entrypoint = [ "${teepot.teepot.vault_unseal}/bin/vault-unseal" ];
|
||||||
|
|
||||||
contents = buildEnv {
|
contents = buildEnv {
|
||||||
name = "image-root";
|
name = "image-root";
|
||||||
paths = with dockerTools; with nixsgx;[
|
paths =
|
||||||
openssl.out
|
with dockerTools;
|
||||||
curl.out
|
with nixsgx;
|
||||||
sgx-dcap.quote_verify
|
[
|
||||||
sgx-dcap.default_qpl
|
openssl.out
|
||||||
usrBinEnv
|
curl.out
|
||||||
binSh
|
usrBinEnv
|
||||||
caCertificates
|
binSh
|
||||||
fakeNss
|
caCertificates
|
||||||
teepot.teepot.vault_unseal
|
fakeNss
|
||||||
];
|
teepot.teepot.vault_unseal
|
||||||
pathsToLink = [ "/bin" "/lib" "/etc" ];
|
] ++ lib.optionals (stdenv.hostPlatform.system == "x86_64-linux") [
|
||||||
};
|
sgx-dcap.quote_verify
|
||||||
}
|
sgx-dcap.default_qpl
|
||||||
|
];
|
||||||
|
pathsToLink = [
|
||||||
|
"/bin"
|
||||||
|
"/lib"
|
||||||
|
"/etc"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -2,30 +2,44 @@
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ dockerTools
|
{ dockerTools
|
||||||
, buildEnv
|
, buildEnv
|
||||||
|
, lib
|
||||||
|
, stdenv
|
||||||
, teepot
|
, teepot
|
||||||
, openssl
|
, openssl
|
||||||
, curl
|
, curl
|
||||||
, nixsgx
|
, nixsgx
|
||||||
}:
|
}:
|
||||||
dockerTools.buildLayeredImage {
|
if (stdenv.hostPlatform.isDarwin) then {
|
||||||
name = "verify-attestation-sgx";
|
# FIXME: dockerTools.buildLayeredImage seems to be broken on Darwin
|
||||||
|
} else
|
||||||
|
dockerTools.buildLayeredImage {
|
||||||
|
name = "verify-attestation-sgx";
|
||||||
|
|
||||||
config.Entrypoint = [ "${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";
|
||||||
|
|
||||||
paths = with dockerTools; with nixsgx;[
|
paths =
|
||||||
openssl.out
|
with dockerTools;
|
||||||
curl.out
|
with nixsgx;
|
||||||
sgx-dcap.quote_verify
|
[
|
||||||
sgx-dcap.default_qpl
|
openssl.out
|
||||||
teepot.teepot.verify_attestation
|
curl.out
|
||||||
usrBinEnv
|
teepot.teepot.verify_attestation
|
||||||
binSh
|
usrBinEnv
|
||||||
caCertificates
|
binSh
|
||||||
fakeNss
|
caCertificates
|
||||||
];
|
fakeNss
|
||||||
pathsToLink = [ "/bin" "/lib" "/etc" "/share" ];
|
] ++ lib.optionals (stdenv.hostPlatform.system == "x86_64-linux") [
|
||||||
};
|
sgx-dcap.quote_verify
|
||||||
}
|
sgx-dcap.default_qpl
|
||||||
|
];
|
||||||
|
pathsToLink = [
|
||||||
|
"/bin"
|
||||||
|
"/lib"
|
||||||
|
"/etc"
|
||||||
|
"/share"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -3,31 +3,45 @@
|
||||||
{ dockerTools
|
{ dockerTools
|
||||||
, buildEnv
|
, buildEnv
|
||||||
, teepot
|
, teepot
|
||||||
|
, stdenv
|
||||||
, openssl
|
, openssl
|
||||||
, curl
|
, curl
|
||||||
, nixsgx
|
, nixsgx
|
||||||
, pkg-config
|
, pkg-config
|
||||||
}:
|
}:
|
||||||
dockerTools.buildLayeredImage {
|
if (stdenv.hostPlatform.isDarwin) then {
|
||||||
name = "verify-era-proof-attestation";
|
# FIXME: dockerTools.buildLayeredImage seems to be broken on Darwin
|
||||||
|
} else
|
||||||
|
dockerTools.buildLayeredImage {
|
||||||
|
name = "verify-era-proof-attestation";
|
||||||
|
|
||||||
config.Entrypoint = [ "${teepot.teepot.verify_era_proof_attestation}/bin/verify-era-proof-attestation" ];
|
config.Entrypoint = [
|
||||||
config.Env = [ "LD_LIBRARY_PATH=/lib" ];
|
"${teepot.teepot.verify_era_proof_attestation}/bin/verify-era-proof-attestation"
|
||||||
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" ];
|
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"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ lib
|
{ lib
|
||||||
, pkgs
|
, pkgs
|
||||||
|
, stdenv
|
||||||
, system
|
, system
|
||||||
, ...
|
, ...
|
||||||
}: lib.teepot.nixosGenerate {
|
}:
|
||||||
|
if (stdenv.hostPlatform.system != "x86_64-linux") then { } else
|
||||||
|
lib.teepot.nixosGenerate {
|
||||||
inherit (lib) nixosSystem;
|
inherit (lib) nixosSystem;
|
||||||
inherit system pkgs;
|
inherit system pkgs;
|
||||||
modules = [
|
modules = [
|
||||||
|
|
|
@ -1,20 +1,30 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ lib, pkgs, makeWrapper, teepot }:
|
{ lib
|
||||||
let teepotCrate = teepot.teepotCrate; in
|
, pkgs
|
||||||
|
, makeWrapper
|
||||||
|
, teepot
|
||||||
|
, stdenv
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
teepotCrate = teepot.teepotCrate;
|
||||||
|
in
|
||||||
teepotCrate.craneLib.buildPackage (
|
teepotCrate.craneLib.buildPackage (
|
||||||
teepotCrate.commonArgs // {
|
teepotCrate.commonArgs
|
||||||
|
// {
|
||||||
pname = "teepot";
|
pname = "teepot";
|
||||||
inherit (teepotCrate) cargoArtifacts;
|
inherit (teepotCrate) cargoArtifacts;
|
||||||
|
|
||||||
nativeBuildInputs = teepotCrate.commonArgs.nativeBuildInputs ++ [ makeWrapper ];
|
nativeBuildInputs = teepotCrate.commonArgs.nativeBuildInputs ++ [ makeWrapper ];
|
||||||
|
|
||||||
passthru = {
|
passthru = {
|
||||||
inherit (teepotCrate) rustPlatform
|
inherit (teepotCrate)
|
||||||
|
rustPlatform
|
||||||
rustVersion
|
rustVersion
|
||||||
commonArgs
|
commonArgs
|
||||||
craneLib
|
craneLib
|
||||||
cargoArtifacts;
|
cargoArtifacts
|
||||||
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = [
|
outputs = [
|
||||||
|
@ -37,9 +47,10 @@ teepotCrate.craneLib.buildPackage (
|
||||||
"verify_era_proof_attestation"
|
"verify_era_proof_attestation"
|
||||||
];
|
];
|
||||||
|
|
||||||
postInstall = ''
|
postInstall = lib.optionalString (stdenv.hostPlatform.system == "x86_64-linux") ''
|
||||||
removeReferencesToVendoredSources "$out" "$cargoVendorDir"
|
removeReferencesToVendoredSources "$out" "$cargoVendorDir"
|
||||||
removeReferencesToVendoredSources "$out" "${teepotCrate.rustVersion}/lib/rustlib/"
|
removeReferencesToVendoredSources "$out" "${teepotCrate.rustVersion}/lib/rustlib/"
|
||||||
|
'' + ''
|
||||||
mkdir -p $out/nix-support
|
mkdir -p $out/nix-support
|
||||||
for i in $outputs; do
|
for i in $outputs; do
|
||||||
[[ $i == "out" ]] && continue
|
[[ $i == "out" ]] && continue
|
||||||
|
@ -47,14 +58,19 @@ teepotCrate.craneLib.buildPackage (
|
||||||
echo -n "''${!i} " >> $out/nix-support/propagated-user-env-packages
|
echo -n "''${!i} " >> $out/nix-support/propagated-user-env-packages
|
||||||
binname=''${i//_/-}
|
binname=''${i//_/-}
|
||||||
mv "$out/bin/$binname" "''${!i}/bin/"
|
mv "$out/bin/$binname" "''${!i}/bin/"
|
||||||
|
'' + lib.optionalString (stdenv.hostPlatform.system == "x86_64-linux") ''
|
||||||
makeWrapper "''${!i}/bin/$binname" "''${!i}/bin/$binname-dcap" \
|
makeWrapper "''${!i}/bin/$binname" "''${!i}/bin/$binname-dcap" \
|
||||||
--prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ pkgs.nixsgx.sgx-dcap.quote_verify pkgs.nixsgx.sgx-dcap.default_qpl pkgs.curl ]}" \
|
--prefix LD_LIBRARY_PATH : "${
|
||||||
--set-default QCNL_CONF_PATH "${pkgs.nixsgx.sgx-dcap.default_qpl}/etc/sgx_default_qcnl.conf"
|
lib.makeLibraryPath [
|
||||||
|
pkgs.nixsgx.sgx-dcap.quote_verify
|
||||||
|
pkgs.nixsgx.sgx-dcap.default_qpl
|
||||||
|
pkgs.curl
|
||||||
|
]
|
||||||
|
}" \
|
||||||
|
--set-default QCNL_CONF_PATH "${pkgs.nixsgx.sgx-dcap.default_qpl}/etc/sgx_default_qcnl.conf"
|
||||||
|
'' + ''
|
||||||
done
|
done
|
||||||
rmdir "$out/bin"
|
rmdir "$out/bin"
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ lib
|
{ lib
|
||||||
, inputs
|
, inputs
|
||||||
|
, stdenv
|
||||||
, makeRustPlatform
|
, makeRustPlatform
|
||||||
, nixsgx
|
, nixsgx ? null
|
||||||
, pkg-config
|
, pkg-config
|
||||||
, rust-bin
|
, rust-bin
|
||||||
, pkgs
|
, pkgs
|
||||||
, openssl
|
, openssl
|
||||||
|
, darwin
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
rustVersion = rust-bin.fromRustupToolchainFile (inputs.src + "/rust-toolchain.toml");
|
rustVersion = rust-bin.fromRustupToolchainFile (inputs.src + "/rust-toolchain.toml");
|
||||||
|
@ -23,11 +25,15 @@ let
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
openssl
|
openssl.dev
|
||||||
|
]
|
||||||
|
++ lib.optionals (stdenv.hostPlatform.system == "x86_64-linux") [
|
||||||
nixsgx.sgx-sdk
|
nixsgx.sgx-sdk
|
||||||
nixsgx.sgx-dcap
|
nixsgx.sgx-dcap
|
||||||
nixsgx.sgx-dcap.quote_verify
|
nixsgx.sgx-dcap.quote_verify
|
||||||
nixsgx.sgx-dcap.libtdx_attest
|
nixsgx.sgx-dcap.libtdx_attest
|
||||||
|
] ++ lib.optionals stdenv.isDarwin [
|
||||||
|
darwin.apple_sdk.frameworks.Security
|
||||||
];
|
];
|
||||||
|
|
||||||
strictDeps = true;
|
strictDeps = true;
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.83"
|
channel = "1.86"
|
||||||
components = ["rustfmt", "clippy", "rust-src"]
|
components = ["rustfmt", "clippy", "rust-src"]
|
||||||
|
|
|
@ -6,11 +6,18 @@
|
||||||
, teepot
|
, teepot
|
||||||
, nixsgx
|
, nixsgx
|
||||||
, stdenv
|
, stdenv
|
||||||
|
,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
toolchain_with_src = (teepot.teepot.passthru.rustVersion.override {
|
toolchain_with_src = (
|
||||||
extensions = [ "rustfmt" "clippy" "rust-src" ];
|
teepot.teepot.passthru.rustVersion.override {
|
||||||
});
|
extensions = [
|
||||||
|
"rustfmt"
|
||||||
|
"clippy"
|
||||||
|
"rust-src"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
);
|
||||||
in
|
in
|
||||||
mkShell {
|
mkShell {
|
||||||
inputsFrom = [ teepot.teepot ];
|
inputsFrom = [ teepot.teepot ];
|
||||||
|
@ -21,26 +28,34 @@ mkShell {
|
||||||
teepot.teepot.passthru.rustPlatform.bindgenHook
|
teepot.teepot.passthru.rustPlatform.bindgenHook
|
||||||
];
|
];
|
||||||
|
|
||||||
packages = with pkgs; [
|
packages =
|
||||||
dive
|
with pkgs;
|
||||||
taplo
|
[
|
||||||
vault
|
dive
|
||||||
cargo-release
|
taplo
|
||||||
google-cloud-sdk-gce
|
vault
|
||||||
azure-cli
|
cargo-release
|
||||||
kubectl
|
azure-cli
|
||||||
kubectx
|
kubectl
|
||||||
k9s
|
kubectx
|
||||||
];
|
k9s
|
||||||
|
google-cloud-sdk
|
||||||
|
];
|
||||||
|
|
||||||
TEE_LD_LIBRARY_PATH = lib.makeLibraryPath [
|
TEE_LD_LIBRARY_PATH = lib.makeLibraryPath (
|
||||||
pkgs.curl
|
lib.optionals (stdenv.hostPlatform.system == "x86_64-linux") [
|
||||||
nixsgx.sgx-dcap
|
pkgs.curl
|
||||||
nixsgx.sgx-dcap.quote_verify
|
nixsgx.sgx-dcap
|
||||||
nixsgx.sgx-dcap.default_qpl
|
nixsgx.sgx-dcap.quote_verify
|
||||||
];
|
nixsgx.sgx-dcap.default_qpl
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
QCNL_CONF_PATH = "${nixsgx.sgx-dcap.default_qpl}/etc/sgx_default_qcnl.conf";
|
QCNL_CONF_PATH =
|
||||||
|
if (stdenv.hostPlatform.system != "x86_64-linux") then
|
||||||
|
""
|
||||||
|
else
|
||||||
|
"${nixsgx.sgx-dcap.default_qpl}/etc/sgx_default_qcnl.conf";
|
||||||
OPENSSL_NO_VENDOR = "1";
|
OPENSSL_NO_VENDOR = "1";
|
||||||
RUST_SRC_PATH = "${toolchain_with_src}/lib/rustlib/src/rust/library";
|
RUST_SRC_PATH = "${toolchain_with_src}/lib/rustlib/src/rust/library";
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue