mirror of
https://github.com/matter-labs/teepot.git
synced 2025-07-21 15:13:56 +02:00
feat: add TDX support
Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
This commit is contained in:
parent
f4fba51e3e
commit
4610475fae
18 changed files with 2316 additions and 369 deletions
184
Cargo.lock
generated
184
Cargo.lock
generated
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "actix-codec"
|
||||
|
@ -311,6 +311,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.8"
|
||||
|
@ -358,6 +367,17 @@ version = "1.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
|
@ -535,6 +555,29 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.59.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"clap 2.34.0",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.65.1"
|
||||
|
@ -849,6 +892,21 @@ dependencies = [
|
|||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags 1.3.2",
|
||||
"strsim 0.8.0",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.17"
|
||||
|
@ -1486,6 +1544,19 @@ dependencies = [
|
|||
"syn 2.0.77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "envy"
|
||||
version = "0.4.2"
|
||||
|
@ -1948,6 +2019,15 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
|
@ -2066,6 +2146,12 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.30"
|
||||
|
@ -2794,7 +2880,7 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
|
@ -4702,6 +4788,12 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
@ -4827,12 +4919,28 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tdx-attest-rs"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/intel/SGXDataCenterAttestationPrimitives.git?rev=aa239d25a437a28f3f4de92c38f5b6809faac842#aa239d25a437a28f3f4de92c38f5b6809faac842"
|
||||
dependencies = [
|
||||
"tdx-attest-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tdx-attest-sys"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/intel/SGXDataCenterAttestationPrimitives.git?rev=aa239d25a437a28f3f4de92c38f5b6809faac842#aa239d25a437a28f3f4de92c38f5b6809faac842"
|
||||
dependencies = [
|
||||
"bindgen 0.59.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tee-key-preexec"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"rand",
|
||||
"secp256k1 0.29.1",
|
||||
"teepot",
|
||||
|
@ -4846,7 +4954,7 @@ name = "tee-ratls-preexec"
|
|||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"rsa",
|
||||
"teepot",
|
||||
"tracing",
|
||||
|
@ -4874,7 +4982,7 @@ version = "0.3.0"
|
|||
dependencies = [
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"serde",
|
||||
"teepot",
|
||||
"tracing",
|
||||
|
@ -4890,7 +4998,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"awc",
|
||||
"bytemuck",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"hex",
|
||||
"rustls 0.22.4",
|
||||
"serde_json",
|
||||
|
@ -4909,7 +5017,7 @@ dependencies = [
|
|||
"actix-web",
|
||||
"anyhow",
|
||||
"awc",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"rustls 0.22.4",
|
||||
"serde_json",
|
||||
"teepot",
|
||||
|
@ -4929,7 +5037,7 @@ dependencies = [
|
|||
"base64 0.22.1",
|
||||
"bytemuck",
|
||||
"bytes",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"const-oid",
|
||||
"enumset",
|
||||
"futures-core",
|
||||
|
@ -4948,12 +5056,15 @@ dependencies = [
|
|||
"serde_with 3.9.0",
|
||||
"sha2",
|
||||
"signature 2.2.0",
|
||||
"tdx-attest-rs",
|
||||
"teepot-tee-quote-verification-rs",
|
||||
"testaso",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-log 0.2.0",
|
||||
"tracing-subscriber",
|
||||
"tracing-test",
|
||||
"webpki-roots",
|
||||
"x509-cert",
|
||||
"zeroize",
|
||||
|
@ -4966,7 +5077,7 @@ dependencies = [
|
|||
"actix-web",
|
||||
"anyhow",
|
||||
"awc",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"serde_json",
|
||||
"teepot",
|
||||
"tracing",
|
||||
|
@ -4989,7 +5100,7 @@ dependencies = [
|
|||
"actix-web",
|
||||
"anyhow",
|
||||
"awc",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"serde_json",
|
||||
"teepot",
|
||||
"tracing",
|
||||
|
@ -5010,6 +5121,15 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.3.0"
|
||||
|
@ -5026,6 +5146,15 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b4d2149a2f578665ca39f8115084635847e9dd6921b5442dcafc7f87bb8e99"
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.63"
|
||||
|
@ -5432,6 +5561,27 @@ dependencies = [
|
|||
"tracing-serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-test"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68"
|
||||
dependencies = [
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
"tracing-test-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-test-macro"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
|
@ -5583,7 +5733,7 @@ dependencies = [
|
|||
"actix-web",
|
||||
"anyhow",
|
||||
"bytemuck",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"hex",
|
||||
"pgp",
|
||||
"serde_json",
|
||||
|
@ -5600,7 +5750,7 @@ dependencies = [
|
|||
"actix-web",
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"serde_json",
|
||||
"teepot",
|
||||
"tracing",
|
||||
|
@ -5614,12 +5764,18 @@ version = "0.2.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "verify-attestation"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"hex",
|
||||
"secp256k1 0.29.1",
|
||||
"teepot",
|
||||
|
@ -5631,7 +5787,7 @@ name = "verify-era-proof-attestation"
|
|||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"clap 4.5.17",
|
||||
"ctrlc",
|
||||
"hex",
|
||||
"jsonrpsee-types",
|
||||
|
|
|
@ -17,18 +17,15 @@ homepage = "https://github.com/matter-labs/teepot"
|
|||
|
||||
[workspace.dependencies]
|
||||
actix-http = "3"
|
||||
actix-tls = "3"
|
||||
actix-web = { version = "4.5", features = ["rustls-0_22"] }
|
||||
anyhow = "1.0.82"
|
||||
awc = { version = "3.4", features = ["rustls-0_22-webpki-roots"] }
|
||||
base64 = "0.22.0"
|
||||
bitflags = "2.5"
|
||||
bytemuck = { version = "1.15.0", features = ["derive", "min_const_generics", "extern_crate_std"] }
|
||||
bytes = "1"
|
||||
clap = { version = "4.5", features = ["std", "derive", "env", "error-context", "help", "usage", "wrap_help"], default-features = false }
|
||||
const-oid = { version = "0.9", default-features = false }
|
||||
ctrlc = "3.4"
|
||||
der = "0.7.9"
|
||||
enumset = { version = "1.1", features = ["serde"] }
|
||||
futures-core = { version = "0.3.30", features = ["alloc"], default-features = false }
|
||||
getrandom = "0.2.14"
|
||||
|
@ -36,7 +33,6 @@ 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" }
|
||||
jsonrpsee-types = { version = "0.23", default-features = false }
|
||||
log = "0.4"
|
||||
num-integer = "0.1.46"
|
||||
num-traits = "0.2.18"
|
||||
p256 = "0.13.2"
|
||||
|
@ -44,17 +40,15 @@ pgp = "0.13"
|
|||
pkcs8 = { version = "0.10" }
|
||||
rand = "0.8"
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
ring = { version = "0.17.8", features = ["std"], default-features = false }
|
||||
rsa = { version = "0.9.6", features = ["sha2", "pem"] }
|
||||
rustls = { version = "0.22" }
|
||||
rustls-pemfile = "2"
|
||||
sec1 = { version = "0.7.3", features = ["der"], default-features = false }
|
||||
secp256k1 = { version = "0.29", features = ["rand-std", "global-context"] }
|
||||
serde = { version = "1", features = ["derive", "rc"] }
|
||||
serde_json = "1"
|
||||
serde_with = { version = "3.8", features = ["base64", "hex"] }
|
||||
sha2 = "0.10.8"
|
||||
signature = "2.2.0"
|
||||
tdx-attest-rs = { version = "0.1.2", git = "https://github.com/intel/SGXDataCenterAttestationPrimitives.git", rev = "aa239d25a437a28f3f4de92c38f5b6809faac842" }
|
||||
teepot = { path = "crates/teepot" }
|
||||
testaso = "0.1.0"
|
||||
thiserror = "1.0.59"
|
||||
|
@ -63,6 +57,7 @@ tracing = "0.1"
|
|||
tracing-actix-web = "0.7"
|
||||
tracing-log = "0.2"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
tracing-test = { version = "0.2.5", features = ["no-env-filter"] }
|
||||
url = "2.5.2"
|
||||
webpki-roots = "0.26.1"
|
||||
x509-cert = { version = "0.2", features = ["builder", "signature"] }
|
||||
|
|
|
@ -9,9 +9,7 @@
|
|||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
use secp256k1::{rand, Keypair, PublicKey, Secp256k1, SecretKey};
|
||||
use std::ffi::OsString;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::Command;
|
||||
use std::{ffi::OsString, os::unix::process::CommandExt, process::Command};
|
||||
use teepot::quote::get_quote;
|
||||
use tracing::error;
|
||||
use tracing_log::LogTracer;
|
||||
|
@ -47,15 +45,15 @@ fn main_with_error() -> Result<()> {
|
|||
let verifying_key = PublicKey::from_keypair(&keypair);
|
||||
let verifying_key_bytes = verifying_key.serialize();
|
||||
let tee_type = match get_quote(verifying_key_bytes.as_ref()) {
|
||||
Ok(quote) => {
|
||||
Ok((tee_type, quote)) => {
|
||||
// save quote to file
|
||||
std::fs::write(TEE_QUOTE_FILE, quote)?;
|
||||
"sgx"
|
||||
tee_type.to_string()
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to get quote: {}", e);
|
||||
std::fs::write(TEE_QUOTE_FILE, [])?;
|
||||
"none"
|
||||
"none".to_string()
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use secp256k1::{ecdsa::Signature, Message, PublicKey};
|
|||
use std::{fs, io::Read, path::PathBuf, str::FromStr, time::UNIX_EPOCH};
|
||||
use teepot::{
|
||||
client::TcbLevel,
|
||||
sgx::{tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult},
|
||||
quote::{error, tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult},
|
||||
};
|
||||
use zksync_basic_types::H256;
|
||||
|
||||
|
@ -84,7 +84,7 @@ fn verify_signature(
|
|||
quote_verification_result: &QuoteVerificationResult,
|
||||
signature_args: &SignatureArgs,
|
||||
) -> Result<()> {
|
||||
let reportdata = "e_verification_result.quote.report_body.reportdata;
|
||||
let reportdata = "e_verification_result.quote.get_report_data();
|
||||
let public_key = PublicKey::from_slice(reportdata)?;
|
||||
println!("Public key from attestation quote: {}", public_key);
|
||||
let signature_bytes = fs::read(&signature_args.signature_file)?;
|
||||
|
@ -103,8 +103,10 @@ fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result<QuoteVerif
|
|||
"Verifying quote ({} bytes)...",
|
||||
attestation_quote_bytes.len()
|
||||
);
|
||||
let collateral =
|
||||
tee_qv_get_collateral(attestation_quote_bytes).context("Failed to get collateral")?;
|
||||
let collateral = error::QuoteContext::context(
|
||||
tee_qv_get_collateral(attestation_quote_bytes),
|
||||
"Failed to get collateral",
|
||||
)?;
|
||||
let unix_time: i64 = std::time::SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)?
|
||||
.as_secs() as _;
|
||||
|
@ -128,7 +130,6 @@ fn print_quote_verification_summary(quote_verification_result: &QuoteVerificatio
|
|||
println!("\tInfo: Advisory ID: {advisory}");
|
||||
}
|
||||
println!("Quote verification result: {}", tcblevel);
|
||||
println!("mrsigner: {}", hex::encode(quote.report_body.mrsigner));
|
||||
println!("mrenclave: {}", hex::encode(quote.report_body.mrenclave));
|
||||
println!("reportdata: {}", hex::encode(quote.report_body.reportdata));
|
||||
|
||||
println!("{:#}", "e.report);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
|
||||
use crate::{args::AttestationPolicyArgs, client::JsonRpcClient};
|
||||
use anyhow::{Context, Result};
|
||||
use hex::encode;
|
||||
use secp256k1::{constants::PUBLIC_KEY_SIZE, ecdsa::Signature, Message, PublicKey};
|
||||
use teepot::{
|
||||
client::TcbLevel,
|
||||
sgx::{tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult},
|
||||
quote::{
|
||||
error::QuoteContext, tee_qv_get_collateral, verify_quote_with_collateral,
|
||||
QuoteVerificationResult, Report,
|
||||
},
|
||||
};
|
||||
use tracing::{debug, info, warn};
|
||||
use zksync_basic_types::{L1BatchNumber, H256};
|
||||
|
||||
use crate::args::AttestationPolicyArgs;
|
||||
use crate::client::JsonRpcClient;
|
||||
|
||||
pub async fn verify_batch_proof(
|
||||
quote_verification_result: &QuoteVerificationResult<'_>,
|
||||
quote_verification_result: &QuoteVerificationResult,
|
||||
attestation_policy: &AttestationPolicyArgs,
|
||||
node_client: &impl JsonRpcClient,
|
||||
signature: &[u8],
|
||||
|
@ -28,7 +29,7 @@ pub async fn verify_batch_proof(
|
|||
let batch_no = batch_number.0;
|
||||
|
||||
let public_key = PublicKey::from_slice(
|
||||
"e_verification_result.quote.report_body.reportdata[..PUBLIC_KEY_SIZE],
|
||||
"e_verification_result.quote.get_report_data()[..PUBLIC_KEY_SIZE],
|
||||
)?;
|
||||
debug!(batch_no, "public key: {}", public_key);
|
||||
|
||||
|
@ -45,8 +46,10 @@ pub async fn verify_batch_proof(
|
|||
}
|
||||
|
||||
pub fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result<QuoteVerificationResult> {
|
||||
let collateral =
|
||||
tee_qv_get_collateral(attestation_quote_bytes).context("Failed to get collateral!")?;
|
||||
let collateral = QuoteContext::context(
|
||||
tee_qv_get_collateral(attestation_quote_bytes),
|
||||
"Failed to get collateral!",
|
||||
)?;
|
||||
let unix_time: i64 = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)?
|
||||
.as_secs() as _;
|
||||
|
@ -66,17 +69,19 @@ pub fn log_quote_verification_summary(quote_verification_result: &QuoteVerificat
|
|||
warn!("Freshly fetched collateral expired!");
|
||||
}
|
||||
let tcblevel = TcbLevel::from(*result);
|
||||
info!(
|
||||
"Quote verification result: {}. mrsigner: {}, mrenclave: {}, reportdata: {}. Advisory IDs: {}.",
|
||||
tcblevel,
|
||||
hex::encode(quote.report_body.mrsigner),
|
||||
hex::encode(quote.report_body.mrenclave),
|
||||
hex::encode(quote.report_body.reportdata),
|
||||
if advisories.is_empty() {
|
||||
let advisories = if advisories.is_empty() {
|
||||
"None".to_string()
|
||||
} else {
|
||||
advisories.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ")
|
||||
}
|
||||
advisories
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
};
|
||||
|
||||
info!(
|
||||
"Quote verification result: {tcblevel}. {report}. Advisory IDs: {advisories}.",
|
||||
report = "e.report
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -88,7 +93,7 @@ fn verify_signature(signature: &[u8], public_key: PublicKey, root_hash: H256) ->
|
|||
|
||||
fn is_quote_matching_policy(
|
||||
attestation_policy: &AttestationPolicyArgs,
|
||||
quote_verification_result: &QuoteVerificationResult<'_>,
|
||||
quote_verification_result: &QuoteVerificationResult,
|
||||
) -> bool {
|
||||
let quote = "e_verification_result.quote;
|
||||
let tcblevel = TcbLevel::from(quote_verification_result.result);
|
||||
|
@ -100,17 +105,21 @@ fn is_quote_matching_policy(
|
|||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
match "e.report {
|
||||
Report::SgxEnclave(report_body) => {
|
||||
check_policy(
|
||||
attestation_policy.sgx_mrsigners.as_deref(),
|
||||
"e.report_body.mrsigner,
|
||||
&report_body.mr_signer,
|
||||
"mrsigner",
|
||||
) && check_policy(
|
||||
attestation_policy.sgx_mrenclaves.as_deref(),
|
||||
"e.report_body.mrenclave,
|
||||
&report_body.mr_enclave,
|
||||
"mrenclave",
|
||||
)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_policy(policy: Option<&str>, actual_value: &[u8], field_name: &str) -> bool {
|
||||
if let Some(valid_values) = policy {
|
||||
|
|
|
@ -37,6 +37,7 @@ serde_json.workspace = true
|
|||
serde_with.workspace = true
|
||||
sha2.workspace = true
|
||||
signature.workspace = true
|
||||
tdx-attest-rs.workspace = true
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-log.workspace = true
|
||||
|
@ -48,5 +49,6 @@ zeroize.workspace = true
|
|||
[dev-dependencies]
|
||||
anyhow.workspace = true
|
||||
base64.workspace = true
|
||||
hex.workspace = true
|
||||
testaso.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing-test.workspace = true
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
|
||||
pub mod vault;
|
||||
|
||||
pub use crate::quote::verify_quote_with_collateral;
|
||||
pub use crate::quote::QuoteVerificationResult;
|
||||
use crate::quote::Report;
|
||||
use crate::server::pki::{RaTlsCollateralExtension, RaTlsQuoteExtension};
|
||||
use crate::sgx::Quote;
|
||||
pub use crate::sgx::{
|
||||
parse_tcb_levels, sgx_ql_qv_result_t, verify_quote_with_collateral, EnumSet,
|
||||
QuoteVerificationResult, TcbLevel,
|
||||
};
|
||||
pub use crate::sgx::{parse_tcb_levels, sgx_ql_qv_result_t, EnumSet, TcbLevel};
|
||||
use actix_web::http::header;
|
||||
use anyhow::Result;
|
||||
use awc::{Client, Connector};
|
||||
|
@ -195,6 +195,10 @@ impl TeeConnection {
|
|||
} = verify_quote_with_collateral(quote_bytes, collateral.as_ref(), current_time)
|
||||
.unwrap();
|
||||
|
||||
let Report::SgxEnclave(report_body) = quote.report else {
|
||||
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 {
|
||||
error!(
|
||||
|
@ -230,10 +234,10 @@ impl TeeConnection {
|
|||
if let Some(mrsigner) = &self.args.sgx_mrsigner {
|
||||
let mrsigner_bytes = hex::decode(mrsigner)
|
||||
.map_err(|e| Error::General(format!("Failed to decode mrsigner: {}", e)))?;
|
||||
if quote.report_body.mrsigner[..] != mrsigner_bytes {
|
||||
if report_body.mr_signer[..] != mrsigner_bytes {
|
||||
return Err(Error::General(format!(
|
||||
"mrsigner mismatch: got {}, expected {}",
|
||||
hex::encode(quote.report_body.mrsigner),
|
||||
hex::encode(report_body.mr_signer),
|
||||
&mrsigner
|
||||
)));
|
||||
} else {
|
||||
|
@ -245,10 +249,10 @@ impl TeeConnection {
|
|||
let mrenclave_bytes = hex::decode(mrenclave).map_err(|e| {
|
||||
Error::General(format!("Failed to decode mrenclave: {}", e))
|
||||
})?;
|
||||
if quote.report_body.mrenclave[..] != mrenclave_bytes {
|
||||
if report_body.mr_enclave[..] != mrenclave_bytes {
|
||||
return Err(Error::General(format!(
|
||||
"mrenclave mismatch: got {}, expected {}",
|
||||
hex::encode(quote.report_body.mrenclave),
|
||||
hex::encode(report_body.mr_enclave),
|
||||
&mrenclave
|
||||
)));
|
||||
} else {
|
||||
|
|
|
@ -7,26 +7,34 @@
|
|||
#![deny(clippy::all)]
|
||||
|
||||
use super::{AttestationArgs, TeeConnection};
|
||||
use crate::json::http::{AuthRequest, AuthResponse};
|
||||
use crate::server::pki::make_self_signed_cert;
|
||||
use crate::server::{AnyHowResponseError, HttpResponseError, Status};
|
||||
pub use crate::sgx::{
|
||||
parse_tcb_levels, sgx_gramine_get_quote, sgx_ql_qv_result_t, tee_qv_get_collateral,
|
||||
verify_quote_with_collateral, Collateral, EnumSet, QuoteVerificationResult, TcbLevel,
|
||||
use crate::{
|
||||
json::http::{AuthRequest, AuthResponse},
|
||||
quote::error::QuoteContext,
|
||||
server::{pki::make_self_signed_cert, AnyHowResponseError, HttpResponseError, Status},
|
||||
};
|
||||
pub use crate::{
|
||||
quote::{verify_quote_with_collateral, QuoteVerificationResult},
|
||||
sgx::{
|
||||
parse_tcb_levels, sgx_gramine_get_quote, sgx_ql_qv_result_t, Collateral, EnumSet, TcbLevel,
|
||||
},
|
||||
};
|
||||
use actix_http::error::PayloadError;
|
||||
use actix_web::http::header;
|
||||
use actix_web::ResponseError;
|
||||
use actix_web::{http::header, ResponseError};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use awc::error::{SendRequestError, StatusCode};
|
||||
use awc::{Client, ClientResponse, Connector};
|
||||
use awc::{
|
||||
error::{SendRequestError, StatusCode},
|
||||
Client, ClientResponse, Connector,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use futures_core::Stream;
|
||||
use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
||||
use rustls::ClientConfig;
|
||||
use serde_json::{json, Value};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::sync::Arc;
|
||||
use std::time;
|
||||
use std::{
|
||||
fmt::{Display, Formatter},
|
||||
sync::Arc,
|
||||
time,
|
||||
};
|
||||
use tracing::{debug, error, info, trace};
|
||||
|
||||
const VAULT_TOKEN_HEADER: &str = "X-Vault-Token";
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
pub mod client;
|
||||
pub mod json;
|
||||
pub mod server;
|
||||
pub mod sgx;
|
||||
|
||||
pub mod log;
|
||||
pub mod quote;
|
||||
pub mod server;
|
||||
pub mod sgx;
|
||||
pub mod tdx;
|
||||
|
|
85
crates/teepot/src/quote/error.rs
Normal file
85
crates/teepot/src/quote/error.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2024 Matter Labs
|
||||
|
||||
//! Quote Error type
|
||||
|
||||
use intel_tee_quote_verification_rs::quote3_error_t;
|
||||
use std::io;
|
||||
use tdx_attest_rs::tdx_attest_error_t;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Quote parsing error
|
||||
#[derive(Error, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum QuoteError {
|
||||
#[error("I/O Error")]
|
||||
IoError { context: String, source: io::Error },
|
||||
#[error("parsing bytes")]
|
||||
ConvertError(#[from] bytemuck::PodCastError),
|
||||
#[error("unsupported quote version")]
|
||||
QuoteVersion,
|
||||
#[error("invalid tee type")]
|
||||
InvalidTeeType,
|
||||
#[error("unsupported body type")]
|
||||
UnsupportedBodyType,
|
||||
#[error("quote verification error {msg}: {inner:?}")]
|
||||
Quote3Error { inner: quote3_error_t, msg: String },
|
||||
#[error("tdx_att_get_quote error {msg}: {inner:?}")]
|
||||
TdxAttGetQuote {
|
||||
inner: tdx_attest_error_t,
|
||||
msg: String,
|
||||
},
|
||||
#[error("invalid version")]
|
||||
InvalidVersion,
|
||||
#[error("report data too long")]
|
||||
ReportDataSize,
|
||||
#[error("can't get a quote: unknown TEE")]
|
||||
UnknownTee,
|
||||
}
|
||||
|
||||
impl From<tdx_attest_error_t> for QuoteError {
|
||||
fn from(code: tdx_attest_error_t) -> Self {
|
||||
Self::TdxAttGetQuote {
|
||||
inner: code,
|
||||
msg: "code".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Usability trait for easy QuoteError annotation
|
||||
pub trait QuoteContext {
|
||||
/// The Ok Type
|
||||
type Ok;
|
||||
/// The Context
|
||||
fn context<I: Into<String>>(self, msg: I) -> Result<Self::Ok, QuoteError>;
|
||||
}
|
||||
|
||||
impl<T> QuoteContext for Result<T, std::io::Error> {
|
||||
type Ok = T;
|
||||
fn context<I: Into<String>>(self, msg: I) -> Result<T, QuoteError> {
|
||||
self.map_err(|e| QuoteError::IoError {
|
||||
context: msg.into(),
|
||||
source: e,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> QuoteContext for Result<T, quote3_error_t> {
|
||||
type Ok = T;
|
||||
fn context<I: Into<String>>(self, msg: I) -> Result<T, QuoteError> {
|
||||
self.map_err(|e| QuoteError::Quote3Error {
|
||||
msg: msg.into(),
|
||||
inner: e,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> QuoteContext for Result<T, tdx_attest_error_t> {
|
||||
type Ok = T;
|
||||
fn context<I: Into<String>>(self, msg: I) -> Result<T, QuoteError> {
|
||||
self.map_err(|e| QuoteError::TdxAttGetQuote {
|
||||
msg: msg.into(),
|
||||
inner: e,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,40 +1,733 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
|
||||
// Parts of it are Copyright (c) 2024 Phala Network
|
||||
// and copied from https://github.com/Phala-Network/dcap-qvl
|
||||
|
||||
//! Get a quote from a TEE
|
||||
pub mod error;
|
||||
|
||||
use crate::sgx::sgx_gramine_get_quote;
|
||||
use std::io;
|
||||
use crate::{
|
||||
quote::error::{QuoteContext as _, QuoteError},
|
||||
sgx::sgx_gramine_get_quote,
|
||||
tdx::tgx_get_quote,
|
||||
};
|
||||
use bytemuck::{cast_slice, AnyBitPattern};
|
||||
use intel_tee_quote_verification_rs::{
|
||||
sgx_ql_qv_result_t, sgx_ql_qv_supplemental_t, tee_get_supplemental_data_version_and_size,
|
||||
tee_supp_data_descriptor_t, tee_verify_quote, Collateral,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
fmt::{Display, Formatter},
|
||||
io::Read,
|
||||
mem,
|
||||
str::FromStr,
|
||||
};
|
||||
use tracing::{trace, warn};
|
||||
|
||||
pub use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[allow(missing_docs)]
|
||||
#[error("{msg}")]
|
||||
pub struct GetQuoteError {
|
||||
pub(crate) msg: Box<str>,
|
||||
#[source] // optional if field name is `source`
|
||||
pub(crate) source: io::Error,
|
||||
pub const TEE_TYPE_SGX: u32 = 0x00000000;
|
||||
#[allow(missing_docs)]
|
||||
pub const TEE_TYPE_TDX: u32 = 0x00000081;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const BODY_SGX_ENCLAVE_REPORT_TYPE: u16 = 1;
|
||||
#[allow(missing_docs)]
|
||||
pub const BODY_TD_REPORT10_TYPE: u16 = 2;
|
||||
#[allow(missing_docs)]
|
||||
pub const BODY_TD_REPORT15_TYPE: u16 = 3;
|
||||
#[allow(missing_docs)]
|
||||
pub const ENCLAVE_REPORT_BYTE_LEN: usize = 384;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const ECDSA_SIGNATURE_BYTE_LEN: usize = 64;
|
||||
#[allow(missing_docs)]
|
||||
pub const ECDSA_PUBKEY_BYTE_LEN: usize = 64;
|
||||
#[allow(missing_docs)]
|
||||
pub const QE_REPORT_SIG_BYTE_LEN: usize = ECDSA_SIGNATURE_BYTE_LEN;
|
||||
|
||||
mod serde_bytes {
|
||||
use serde::Deserialize;
|
||||
|
||||
pub(crate) trait FromBytes {
|
||||
fn from_bytes(bytes: Vec<u8>) -> Option<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
impl FromBytes for Vec<u8> {
|
||||
fn from_bytes(bytes: Vec<u8>) -> Option<Self> {
|
||||
Some(bytes)
|
||||
}
|
||||
}
|
||||
impl<const N: usize> FromBytes for [u8; N] {
|
||||
fn from_bytes(bytes: Vec<u8>) -> Option<Self> {
|
||||
bytes.try_into().ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn serialize<S: serde::Serializer>(
|
||||
data: impl AsRef<[u8]>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
let hex_str = hex::encode(data);
|
||||
serializer.serialize_str(&hex_str)
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize<'de, D: serde::Deserializer<'de>, T: FromBytes>(
|
||||
deserializer: D,
|
||||
) -> Result<T, D::Error> {
|
||||
let hex_str = String::deserialize(deserializer)?;
|
||||
let bytes = hex::decode(hex_str).map_err(serde::de::Error::custom)?;
|
||||
T::from_bytes(bytes).ok_or_else(|| serde::de::Error::custom("invalid bytes"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that allows zero-copy read of value-references from slices in LE format.
|
||||
pub trait Decode: Sized {
|
||||
/// Attempt to deserialise the value from input.
|
||||
fn decode<I: Read>(input: &mut I) -> Result<Self, error::QuoteError>;
|
||||
}
|
||||
|
||||
impl<T: AnyBitPattern> Decode for T {
|
||||
fn decode<I: Read>(input: &mut I) -> Result<Self, error::QuoteError> {
|
||||
let mut bytes = vec![0u8; size_of::<T>()];
|
||||
input.read(&mut bytes).context("parsing bytes")?;
|
||||
bytemuck::try_pod_read_unaligned(&bytes).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C)]
|
||||
pub struct Data<T> {
|
||||
pub data: Vec<u8>,
|
||||
_marker: core::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Serialize for Data<T> {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serde_bytes::serialize(&self.data, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Deserialize<'de> for Data<T> {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let data = serde_bytes::deserialize(deserializer)?;
|
||||
Ok(Data {
|
||||
data,
|
||||
_marker: core::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode + Into<u64>> Decode for Data<T> {
|
||||
fn decode<I: Read>(input: &mut I) -> Result<Self, QuoteError> {
|
||||
let len = T::decode(input)?;
|
||||
let mut data = vec![0u8; len.into() as usize];
|
||||
input.read(&mut data).context("reading bytes")?;
|
||||
Ok(Data {
|
||||
data,
|
||||
_marker: core::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(AnyBitPattern, Debug, Serialize, Deserialize, Copy, Clone)]
|
||||
#[repr(C, packed)]
|
||||
pub struct Header {
|
||||
pub version: u16,
|
||||
pub attestation_key_type: u16,
|
||||
pub tee_type: u32,
|
||||
pub qe_svn: u16,
|
||||
pub pce_svn: u16,
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub qe_vendor_id: [u8; 16],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub user_data: [u8; 20],
|
||||
}
|
||||
|
||||
#[derive(AnyBitPattern, Debug, Copy, Clone)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C, packed)]
|
||||
pub struct Body {
|
||||
pub body_type: u16,
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, AnyBitPattern, Debug, Clone, Copy)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C, packed)]
|
||||
pub struct EnclaveReport {
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub cpu_svn: [u8; 16],
|
||||
pub misc_select: u32,
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub reserved1: [u8; 28],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub attributes: [u8; 16],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub mr_enclave: [u8; 32],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub reserved2: [u8; 32],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub mr_signer: [u8; 32],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub reserved3: [u8; 96],
|
||||
pub isv_prod_id: u16,
|
||||
pub isv_svn: u16,
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub reserved4: [u8; 60],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub report_data: [u8; 64],
|
||||
}
|
||||
|
||||
#[derive(AnyBitPattern, Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C, packed)]
|
||||
pub struct TDReport10 {
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub tee_tcb_svn: [u8; 16],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub mr_seam: [u8; 48],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub mr_signer_seam: [u8; 48],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub seam_attributes: [u8; 8],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub td_attributes: [u8; 8],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub xfam: [u8; 8],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub mr_td: [u8; 48],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub mr_config_id: [u8; 48],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub mr_owner: [u8; 48],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub mr_owner_config: [u8; 48],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub rt_mr0: [u8; 48],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub rt_mr1: [u8; 48],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub rt_mr2: [u8; 48],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub rt_mr3: [u8; 48],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub report_data: [u8; 64],
|
||||
}
|
||||
|
||||
#[derive(AnyBitPattern, Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C, packed)]
|
||||
pub struct TDReport15 {
|
||||
pub base: TDReport10,
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub tee_tcb_svn2: [u8; 16],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub mr_service_td: [u8; 48],
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct CertificationData {
|
||||
pub cert_type: u16,
|
||||
pub body: Data<u32>,
|
||||
}
|
||||
|
||||
impl Decode for CertificationData {
|
||||
fn decode<I: Read>(input: &mut I) -> Result<Self, QuoteError> {
|
||||
Ok(Self {
|
||||
cert_type: Decode::decode(input)?,
|
||||
body: Decode::decode(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for CertificationData {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let body_str = String::from_utf8_lossy(&self.body.data);
|
||||
f.debug_struct("CertificationData")
|
||||
.field("cert_type", &self.cert_type)
|
||||
.field("body", &body_str)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C)]
|
||||
pub struct QEReportCertificationData {
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub qe_report: [u8; ENCLAVE_REPORT_BYTE_LEN],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub qe_report_signature: [u8; QE_REPORT_SIG_BYTE_LEN],
|
||||
pub qe_auth_data: Data<u16>,
|
||||
pub certification_data: CertificationData,
|
||||
}
|
||||
|
||||
impl Decode for QEReportCertificationData {
|
||||
fn decode<I: Read>(input: &mut I) -> Result<Self, QuoteError> {
|
||||
Ok(Self {
|
||||
qe_report: Decode::decode(input)?,
|
||||
qe_report_signature: Decode::decode(input)?,
|
||||
qe_auth_data: Decode::decode(input)?,
|
||||
certification_data: Decode::decode(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C)]
|
||||
pub struct AuthDataV3 {
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub ecdsa_signature: [u8; ECDSA_SIGNATURE_BYTE_LEN],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub ecdsa_attestation_key: [u8; ECDSA_PUBKEY_BYTE_LEN],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub qe_report: [u8; ENCLAVE_REPORT_BYTE_LEN],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub qe_report_signature: [u8; QE_REPORT_SIG_BYTE_LEN],
|
||||
pub qe_auth_data: Data<u16>,
|
||||
pub certification_data: CertificationData,
|
||||
}
|
||||
|
||||
impl Decode for AuthDataV3 {
|
||||
fn decode<I: Read>(input: &mut I) -> Result<Self, QuoteError> {
|
||||
Ok(Self {
|
||||
ecdsa_signature: Decode::decode(input)?,
|
||||
ecdsa_attestation_key: Decode::decode(input)?,
|
||||
qe_report: Decode::decode(input)?,
|
||||
qe_report_signature: Decode::decode(input)?,
|
||||
qe_auth_data: Decode::decode(input)?,
|
||||
certification_data: Decode::decode(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C)]
|
||||
pub struct AuthDataV4 {
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub ecdsa_signature: [u8; ECDSA_SIGNATURE_BYTE_LEN],
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub ecdsa_attestation_key: [u8; ECDSA_PUBKEY_BYTE_LEN],
|
||||
pub certification_data: CertificationData,
|
||||
pub qe_report_data: QEReportCertificationData,
|
||||
}
|
||||
|
||||
impl AuthDataV4 {
|
||||
#[allow(missing_docs)]
|
||||
pub fn into_v3(self) -> AuthDataV3 {
|
||||
AuthDataV3 {
|
||||
ecdsa_signature: self.ecdsa_signature,
|
||||
ecdsa_attestation_key: self.ecdsa_attestation_key,
|
||||
qe_report: self.qe_report_data.qe_report,
|
||||
qe_report_signature: self.qe_report_data.qe_report_signature,
|
||||
qe_auth_data: self.qe_report_data.qe_auth_data,
|
||||
certification_data: self.qe_report_data.certification_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for AuthDataV4 {
|
||||
fn decode<I: Read>(input: &mut I) -> Result<Self, error::QuoteError> {
|
||||
let ecdsa_signature = Decode::decode(input)?;
|
||||
let ecdsa_attestation_key = Decode::decode(input)?;
|
||||
let certification_data: CertificationData = Decode::decode(input)?;
|
||||
let qe_report_data =
|
||||
QEReportCertificationData::decode(&mut &certification_data.body.data[..])?;
|
||||
Ok(AuthDataV4 {
|
||||
ecdsa_signature,
|
||||
ecdsa_attestation_key,
|
||||
certification_data,
|
||||
qe_report_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C)]
|
||||
pub enum AuthData {
|
||||
V3(AuthDataV3),
|
||||
V4(AuthDataV4),
|
||||
}
|
||||
|
||||
impl AuthData {
|
||||
#[allow(missing_docs)]
|
||||
pub fn into_v3(self) -> AuthDataV3 {
|
||||
match self {
|
||||
AuthData::V3(data) => data,
|
||||
AuthData::V4(data) => data.into_v3(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_auth_data(ver: u16, input: &mut &[u8]) -> Result<AuthData, error::QuoteError> {
|
||||
match ver {
|
||||
3 => {
|
||||
let auth_data = AuthDataV3::decode(input)?;
|
||||
Ok(AuthData::V3(auth_data))
|
||||
}
|
||||
4 => {
|
||||
let auth_data = AuthDataV4::decode(input)?;
|
||||
Ok(AuthData::V4(auth_data))
|
||||
}
|
||||
_ => Err(error::QuoteError::QuoteVersion),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C)]
|
||||
#[non_exhaustive]
|
||||
pub enum Report {
|
||||
SgxEnclave(EnclaveReport),
|
||||
TD10(TDReport10),
|
||||
TD15(TDReport15),
|
||||
}
|
||||
|
||||
impl Report {
|
||||
#[allow(missing_docs)]
|
||||
pub fn is_sgx(&self) -> bool {
|
||||
matches!(self, Report::SgxEnclave(_))
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn as_td10(&self) -> Option<&TDReport10> {
|
||||
match self {
|
||||
Report::TD10(report) => Some(report),
|
||||
Report::TD15(report) => Some(&report.base),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn as_td15(&self) -> Option<&TDReport15> {
|
||||
match self {
|
||||
Report::TD15(report) => Some(report),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn as_sgx(&self) -> Option<&EnclaveReport> {
|
||||
match self {
|
||||
Report::SgxEnclave(report) => Some(report),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Report {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
fn space_or_newline(f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if f.alternate() {
|
||||
writeln!(f)
|
||||
} else {
|
||||
write!(f, " ")
|
||||
}
|
||||
}
|
||||
match self {
|
||||
Report::SgxEnclave(report_body) => {
|
||||
write!(f, "mrsigner: {}", hex::encode(report_body.mr_signer))?;
|
||||
space_or_newline(f)?;
|
||||
write!(f, "mrenclave: {}", hex::encode(report_body.mr_enclave))?;
|
||||
space_or_newline(f)?;
|
||||
write!(
|
||||
f,
|
||||
"reportdata: {}",
|
||||
hex::encode(report_body.report_data.as_slice())
|
||||
)?;
|
||||
}
|
||||
Report::TD10(report_body) => {
|
||||
write!(f, "mrtd: {}", hex::encode(report_body.mr_td))?;
|
||||
space_or_newline(f)?;
|
||||
write!(f, "rtmr0: {}", hex::encode(report_body.rt_mr0))?;
|
||||
space_or_newline(f)?;
|
||||
write!(f, "rtmr1: {}", hex::encode(report_body.rt_mr1))?;
|
||||
space_or_newline(f)?;
|
||||
write!(f, "rtmr2: {}", hex::encode(report_body.rt_mr2))?;
|
||||
space_or_newline(f)?;
|
||||
write!(f, "rtmr3: {}", hex::encode(report_body.rt_mr3))?;
|
||||
space_or_newline(f)?;
|
||||
write!(
|
||||
f,
|
||||
"reportdata: {}",
|
||||
hex::encode(report_body.report_data.as_slice())
|
||||
)?;
|
||||
}
|
||||
Report::TD15(report_body) => {
|
||||
let report_body = &report_body.base;
|
||||
write!(f, "mrtd: {}", hex::encode(report_body.mr_td))?;
|
||||
space_or_newline(f)?;
|
||||
write!(f, "rtmr0: {}", hex::encode(report_body.rt_mr0))?;
|
||||
space_or_newline(f)?;
|
||||
write!(f, "rtmr1: {}", hex::encode(report_body.rt_mr1))?;
|
||||
space_or_newline(f)?;
|
||||
write!(f, "rtmr2: {}", hex::encode(report_body.rt_mr2))?;
|
||||
space_or_newline(f)?;
|
||||
write!(f, "rtmr3: {}", hex::encode(report_body.rt_mr3))?;
|
||||
space_or_newline(f)?;
|
||||
write!(
|
||||
f,
|
||||
"reportdata: {}",
|
||||
hex::encode(report_body.report_data.as_slice())
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C)]
|
||||
pub struct Quote {
|
||||
pub header: Header,
|
||||
pub report: Report,
|
||||
pub auth_data: AuthData,
|
||||
}
|
||||
|
||||
impl Decode for Quote {
|
||||
fn decode<I: Read>(input: &mut I) -> Result<Self, error::QuoteError> {
|
||||
let header = Header::decode(input)?;
|
||||
trace!(?header);
|
||||
let report;
|
||||
match header.version {
|
||||
3 => {
|
||||
if header.tee_type != TEE_TYPE_SGX {
|
||||
return Err(error::QuoteError::InvalidTeeType);
|
||||
}
|
||||
report = Report::SgxEnclave(EnclaveReport::decode(input)?);
|
||||
}
|
||||
4 => match header.tee_type {
|
||||
TEE_TYPE_SGX => {
|
||||
report = Report::SgxEnclave(EnclaveReport::decode(input)?);
|
||||
}
|
||||
TEE_TYPE_TDX => {
|
||||
report = Report::TD10(TDReport10::decode(input)?);
|
||||
}
|
||||
_ => return Err(error::QuoteError::InvalidTeeType),
|
||||
},
|
||||
5 => {
|
||||
let body = Body::decode(input)?;
|
||||
match body.body_type {
|
||||
BODY_SGX_ENCLAVE_REPORT_TYPE => {
|
||||
report = Report::SgxEnclave(EnclaveReport::decode(input)?);
|
||||
}
|
||||
BODY_TD_REPORT10_TYPE => {
|
||||
report = Report::TD10(TDReport10::decode(input)?);
|
||||
}
|
||||
BODY_TD_REPORT15_TYPE => {
|
||||
report = Report::TD15(TDReport15::decode(input)?);
|
||||
}
|
||||
_ => return Err(error::QuoteError::UnsupportedBodyType),
|
||||
}
|
||||
}
|
||||
_ => return Err(error::QuoteError::QuoteVersion),
|
||||
}
|
||||
let data = Data::<u32>::decode(input)?;
|
||||
let auth_data = decode_auth_data(header.version, &mut &data.data[..])?;
|
||||
Ok(Quote {
|
||||
header,
|
||||
report,
|
||||
auth_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Quote {
|
||||
/// Parse a TEE quote from a byte slice.
|
||||
pub fn parse(quote: &[u8]) -> Result<Self, QuoteError> {
|
||||
let mut input = quote;
|
||||
let quote = Quote::decode(&mut input)?;
|
||||
Ok(quote)
|
||||
}
|
||||
|
||||
/// Get the report data
|
||||
pub fn get_report_data(&self) -> &[u8] {
|
||||
match &self.report {
|
||||
Report::SgxEnclave(r) => r.report_data.as_slice(),
|
||||
Report::TD10(r) => r.report_data.as_slice(),
|
||||
Report::TD15(r) => r.base.report_data.as_slice(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TEE type
|
||||
#[non_exhaustive]
|
||||
pub enum TEEType {
|
||||
/// Intel SGX
|
||||
SGX,
|
||||
/// Intel TDX
|
||||
TDX,
|
||||
/// AMD SEV-SNP
|
||||
SNP,
|
||||
}
|
||||
|
||||
impl Display for TEEType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = match self {
|
||||
TEEType::SGX => "sgx",
|
||||
TEEType::TDX => "tdx",
|
||||
TEEType::SNP => "snp",
|
||||
};
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for TEEType {
|
||||
type Err = String;
|
||||
fn from_str(s: &str) -> Result<Self, String> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"sgx" => Ok(TEEType::SGX),
|
||||
"tdx" => Ok(TEEType::TDX),
|
||||
"snp" => Ok(TEEType::SNP),
|
||||
_ => Err("Invalid TEE type".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the attestation quote from a TEE
|
||||
pub fn get_quote(report_data: &[u8]) -> Result<Box<[u8]>, GetQuoteError> {
|
||||
pub fn get_quote(report_data: &[u8]) -> Result<(TEEType, Box<[u8]>), QuoteError> {
|
||||
// check, if we are running in a TEE
|
||||
if std::fs::metadata("/dev/attestation").is_ok() {
|
||||
if report_data.len() > 64 {
|
||||
return Err(GetQuoteError {
|
||||
msg: "Report data too long".into(),
|
||||
source: io::Error::new(io::ErrorKind::Other, "Report data too long"),
|
||||
});
|
||||
return Err(QuoteError::ReportDataSize);
|
||||
}
|
||||
|
||||
let mut report_data_fixed = [0u8; 64];
|
||||
report_data_fixed[..report_data.len()].copy_from_slice(report_data);
|
||||
|
||||
sgx_gramine_get_quote(&report_data_fixed)
|
||||
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(GetQuoteError {
|
||||
msg: "Not running in a TEE".into(),
|
||||
source: io::Error::new(io::ErrorKind::Other, "Not running in a TEE"),
|
||||
})
|
||||
Err(QuoteError::UnknownTee)
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of the quote verification
|
||||
pub struct QuoteVerificationResult {
|
||||
/// the raw result
|
||||
pub result: sgx_ql_qv_result_t,
|
||||
/// indicates if the collateral is expired
|
||||
pub collateral_expired: bool,
|
||||
/// the earliest expiration date of the collateral
|
||||
pub earliest_expiration_date: i64,
|
||||
/// Date of the TCB level
|
||||
pub tcb_level_date_tag: i64,
|
||||
/// the advisory string
|
||||
pub advisories: Vec<String>,
|
||||
/// the quote
|
||||
pub quote: Quote,
|
||||
}
|
||||
|
||||
/// Verifies a quote with optional collateral material
|
||||
pub fn verify_quote_with_collateral(
|
||||
quote: &[u8],
|
||||
collateral: Option<&Collateral>,
|
||||
current_time: i64,
|
||||
) -> Result<QuoteVerificationResult, QuoteError> {
|
||||
let mut supp_data: mem::MaybeUninit<sgx_ql_qv_supplemental_t> = 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| 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)
|
||||
}
|
||||
|
|
|
@ -3,17 +3,22 @@
|
|||
|
||||
//! Common attestation API for all TEEs
|
||||
|
||||
use crate::client::AttestationArgs;
|
||||
use crate::json::http::AttestationResponse;
|
||||
use crate::sgx::{
|
||||
parse_tcb_levels, sgx_gramine_get_quote, tee_qv_get_collateral, verify_quote_with_collateral,
|
||||
Collateral, EnumSet, QuoteVerificationResult, TcbLevel,
|
||||
use crate::{
|
||||
client::AttestationArgs,
|
||||
json::http::AttestationResponse,
|
||||
quote::{
|
||||
error::QuoteContext, get_quote, verify_quote_with_collateral, QuoteVerificationResult,
|
||||
},
|
||||
sgx::{parse_tcb_levels, Collateral, EnumSet, TcbLevel},
|
||||
};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use clap::Args;
|
||||
use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
use std::{
|
||||
sync::{Arc, RwLock},
|
||||
time::{Duration, UNIX_EPOCH},
|
||||
};
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
struct Attestation {
|
||||
|
@ -53,7 +58,7 @@ pub fn get_quote_and_collateral(
|
|||
}
|
||||
}
|
||||
|
||||
let myquote = sgx_gramine_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 QuoteVerificationResult {
|
||||
|
@ -89,8 +94,8 @@ pub fn get_quote_and_collateral(
|
|||
"Earliest expiration in {:?}",
|
||||
Duration::from_secs((earliest_expiration_date - unix_time) as _)
|
||||
);
|
||||
info!("mrsigner: {}", hex::encode(quote.report_body.mrsigner));
|
||||
info!("mrenclave: {}", hex::encode(quote.report_body.mrenclave));
|
||||
|
||||
info!("{:#}", quote.report);
|
||||
|
||||
let quote: Arc<[u8]> = Arc::from(myquote);
|
||||
let collateral = Arc::from(collateral);
|
||||
|
|
|
@ -3,39 +3,38 @@
|
|||
|
||||
//! Create a private key and a signed and self-signed certificates
|
||||
|
||||
use crate::quote::get_quote;
|
||||
use crate::sgx::tee_qv_get_collateral;
|
||||
pub use crate::sgx::{
|
||||
parse_tcb_levels, sgx_ql_qv_result_t, verify_quote_with_collateral, EnumSet,
|
||||
QuoteVerificationResult, TcbLevel,
|
||||
};
|
||||
use crate::quote::{error::QuoteContext, get_quote};
|
||||
pub use crate::sgx::{parse_tcb_levels, sgx_ql_qv_result_t, EnumSet, TcbLevel};
|
||||
use anyhow::{Context, Result};
|
||||
use const_oid::db::rfc5280::{ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH};
|
||||
use const_oid::AssociatedOid;
|
||||
use const_oid::{
|
||||
db::rfc5280::{ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH},
|
||||
AssociatedOid,
|
||||
};
|
||||
use getrandom::getrandom;
|
||||
use p256::ecdsa::DerSignature;
|
||||
use p256::pkcs8::EncodePrivateKey;
|
||||
use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
||||
use p256::{ecdsa::DerSignature, pkcs8::EncodePrivateKey};
|
||||
use pkcs8::der;
|
||||
use rustls::pki_types::PrivatePkcs8KeyDer;
|
||||
use sha2::{Digest, Sha256};
|
||||
use signature::Signer;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use std::{str::FromStr, time::Duration};
|
||||
use tracing::debug;
|
||||
use x509_cert::builder::{Builder, CertificateBuilder, Profile};
|
||||
use x509_cert::der::pem::LineEnding;
|
||||
use x509_cert::der::{asn1::OctetString, Encode as _, EncodePem as _, Length};
|
||||
use x509_cert::ext::pkix::name::GeneralNames;
|
||||
use x509_cert::ext::pkix::{ExtendedKeyUsage, SubjectAltName};
|
||||
use x509_cert::ext::{AsExtension, Extension};
|
||||
use x509_cert::name::{Name, RdnSequence};
|
||||
use x509_cert::serial_number::SerialNumber;
|
||||
use x509_cert::spki::{
|
||||
DynSignatureAlgorithmIdentifier, EncodePublicKey, ObjectIdentifier, SignatureBitStringEncoding,
|
||||
SubjectPublicKeyInfoOwned,
|
||||
use x509_cert::{
|
||||
builder::{Builder, CertificateBuilder, Profile},
|
||||
der::{asn1::OctetString, pem::LineEnding, Encode as _, EncodePem as _, Length},
|
||||
ext::{
|
||||
pkix::{name::GeneralNames, ExtendedKeyUsage, SubjectAltName},
|
||||
AsExtension, Extension,
|
||||
},
|
||||
name::{Name, RdnSequence},
|
||||
serial_number::SerialNumber,
|
||||
spki::{
|
||||
DynSignatureAlgorithmIdentifier, EncodePublicKey, ObjectIdentifier,
|
||||
SignatureBitStringEncoding, SubjectPublicKeyInfoOwned,
|
||||
},
|
||||
time::Validity,
|
||||
Certificate,
|
||||
};
|
||||
use x509_cert::time::Validity;
|
||||
use x509_cert::Certificate;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
/// The OID for the `gramine-ra-tls` quote extension
|
||||
|
@ -148,7 +147,7 @@ pub fn make_self_signed_cert(
|
|||
let hash = Sha256::digest(verifying_key_der.as_bytes());
|
||||
key_hash[..32].copy_from_slice(&hash);
|
||||
|
||||
let quote = get_quote(&key_hash)?;
|
||||
let (_tee_type, quote) = get_quote(&key_hash)?;
|
||||
debug!("quote.len: {:?}", quote.len());
|
||||
// Create a relative distinguished name.
|
||||
let rdns = RdnSequence::from_str(dn)?;
|
||||
|
@ -185,6 +184,7 @@ pub fn make_self_signed_cert(
|
|||
.context("failed to add SubjectAltName")?;
|
||||
}
|
||||
|
||||
// FIXME: OID for tee_type
|
||||
builder
|
||||
.add_extension(&RaTlsQuoteExtension {
|
||||
quote: quote.to_vec(),
|
||||
|
@ -234,7 +234,7 @@ where
|
|||
let hash = Sha256::digest(verifying_key_der.as_bytes());
|
||||
key_hash[..32].copy_from_slice(&hash);
|
||||
|
||||
let quote = get_quote(&key_hash).context("Failed to get own quote")?;
|
||||
let (_tee_type, quote) = get_quote(&key_hash).context("Failed to get own quote")?;
|
||||
|
||||
// Create a relative distinguished name.
|
||||
let subject = Name::from_str(dn)?;
|
||||
|
@ -269,6 +269,7 @@ where
|
|||
.context("failed to add SubjectAltName")?;
|
||||
}
|
||||
|
||||
// FIXME: oid according to tee_type
|
||||
builder
|
||||
.add_extension(&RaTlsQuoteExtension {
|
||||
quote: quote.to_vec(),
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023 Matter Labs
|
||||
|
||||
//! Intel SGX Enclave error wrapper
|
||||
|
||||
use bytemuck::PodCastError;
|
||||
use intel_tee_quote_verification_rs::quote3_error_t;
|
||||
use std::fmt::Formatter;
|
||||
|
||||
/// Wrapper for the quote verification Error
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Quote3Error {
|
||||
/// error message
|
||||
pub msg: &'static str,
|
||||
/// raw error code
|
||||
pub inner: quote3_error_t,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Quote3Error {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}: {:?}", self.msg, self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Quote3Error {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}: {:?}", self.msg, self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Quote3Error {}
|
||||
|
||||
impl From<quote3_error_t> for Quote3Error {
|
||||
fn from(inner: quote3_error_t) -> Self {
|
||||
Self {
|
||||
msg: "Generic",
|
||||
inner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum QuoteFromError {
|
||||
#[error(transparent)]
|
||||
PodCastError(#[from] PodCastError),
|
||||
|
||||
#[error("Quote version is invalid")]
|
||||
InvalidVersion,
|
||||
}
|
|
@ -5,24 +5,18 @@
|
|||
|
||||
//! Intel SGX Enclave report structures.
|
||||
|
||||
pub mod error;
|
||||
pub mod sign;
|
||||
pub mod tcblevel;
|
||||
|
||||
use bytemuck::{cast_slice, try_from_bytes, AnyBitPattern, PodCastError};
|
||||
use intel_tee_quote_verification_rs::{
|
||||
quote3_error_t, sgx_ql_qv_supplemental_t, tee_get_supplemental_data_version_and_size,
|
||||
tee_supp_data_descriptor_t, tee_verify_quote,
|
||||
};
|
||||
use std::ffi::CStr;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem;
|
||||
use tracing::{trace, warn};
|
||||
|
||||
use crate::quote::GetQuoteError;
|
||||
pub use error::{Quote3Error, QuoteFromError};
|
||||
use crate::quote::error::QuoteContext;
|
||||
pub use crate::quote::error::QuoteError;
|
||||
use bytemuck::{try_from_bytes, AnyBitPattern, PodCastError};
|
||||
pub use intel_tee_quote_verification_rs::{sgx_ql_qv_result_t, Collateral};
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
io::{Read, Write},
|
||||
mem,
|
||||
};
|
||||
pub use tcblevel::{parse_tcb_levels, EnumSet, TcbLevel};
|
||||
|
||||
/// Structure of a quote
|
||||
|
@ -43,13 +37,13 @@ pub struct Quote {
|
|||
|
||||
impl Quote {
|
||||
/// Creates a quote from a byte slice
|
||||
pub fn try_from_bytes(bytes: &[u8]) -> Result<&Self, QuoteFromError> {
|
||||
pub fn try_from_bytes(bytes: &[u8]) -> Result<&Self, QuoteError> {
|
||||
if bytes.len() < mem::size_of::<Self>() {
|
||||
return Err(PodCastError::SizeMismatch.into());
|
||||
}
|
||||
let this: &Self = try_from_bytes(&bytes[..mem::size_of::<Self>()])?;
|
||||
if this.version() != 3 {
|
||||
return Err(QuoteFromError::InvalidVersion);
|
||||
return Err(QuoteError::InvalidVersion);
|
||||
}
|
||||
Ok(this)
|
||||
}
|
||||
|
@ -98,152 +92,26 @@ pub struct ReportBody {
|
|||
pub reportdata: [u8; 64],
|
||||
}
|
||||
|
||||
/// The result of the quote verification
|
||||
pub struct QuoteVerificationResult<'a> {
|
||||
/// the raw result
|
||||
pub result: sgx_ql_qv_result_t,
|
||||
/// indicates if the collateral is expired
|
||||
pub collateral_expired: bool,
|
||||
/// the earliest expiration date of the collateral
|
||||
pub earliest_expiration_date: i64,
|
||||
/// Date of the TCB level
|
||||
pub tcb_level_date_tag: i64,
|
||||
/// the advisory string
|
||||
pub advisories: Vec<String>,
|
||||
/// the quote
|
||||
pub quote: &'a Quote,
|
||||
}
|
||||
|
||||
/// Verifies a quote with optional collateral material
|
||||
pub fn verify_quote_with_collateral<'a>(
|
||||
quote: &'a [u8],
|
||||
collateral: Option<&Collateral>,
|
||||
current_time: i64,
|
||||
) -> Result<QuoteVerificationResult<'a>, Quote3Error> {
|
||||
let mut supp_data: mem::MaybeUninit<sgx_ql_qv_supplemental_t> = 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| Quote3Error {
|
||||
msg: "tee_get_supplemental_data_version_and_size",
|
||||
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).map_err(
|
||||
|e| Quote3Error {
|
||||
msg: "tee_verify_quote",
|
||||
inner: e,
|
||||
},
|
||||
)?;
|
||||
|
||||
// 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)
|
||||
};
|
||||
|
||||
let quote = Quote::try_from_bytes(quote).map_err(|_| Quote3Error {
|
||||
msg: "Quote::try_from_bytes",
|
||||
inner: quote3_error_t::SGX_QL_QUOTE_FORMAT_UNSUPPORTED,
|
||||
})?;
|
||||
|
||||
let res = QuoteVerificationResult {
|
||||
collateral_expired: collateral_expiration_status != 0,
|
||||
earliest_expiration_date,
|
||||
tcb_level_date_tag,
|
||||
result,
|
||||
quote,
|
||||
advisories,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Get the attestation report in a Gramine enclave
|
||||
pub fn sgx_gramine_get_quote(report_data: &[u8; 64]) -> Result<Box<[u8]>, GetQuoteError> {
|
||||
pub fn sgx_gramine_get_quote(report_data: &[u8; 64]) -> Result<Box<[u8]>, QuoteError> {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.open("/dev/attestation/user_report_data")
|
||||
.map_err(|e| GetQuoteError {
|
||||
msg: "Failed to open `/dev/attestation/user_report_data`".into(),
|
||||
source: e,
|
||||
})?;
|
||||
.context("opening `/dev/attestation/user_report_data`")?;
|
||||
|
||||
file.write(report_data).map_err(|e| GetQuoteError {
|
||||
msg: "Failed to write `/dev/attestation/user_report_data`".into(),
|
||||
source: e,
|
||||
})?;
|
||||
file.write(report_data)
|
||||
.context("writing `/dev/attestation/user_report_data`")?;
|
||||
|
||||
drop(file);
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open("/dev/attestation/quote")
|
||||
.map_err(|e| GetQuoteError {
|
||||
msg: "Failed to open `/dev/attestation/quote`".into(),
|
||||
source: e,
|
||||
})?;
|
||||
.context("opening `/dev/attestation/quote`")?;
|
||||
|
||||
let mut quote = Vec::new();
|
||||
file.read_to_end(&mut quote).map_err(|e| GetQuoteError {
|
||||
msg: "Failed to read `/dev/attestation/quote`".into(),
|
||||
source: e,
|
||||
})?;
|
||||
file.read_to_end(&mut quote)
|
||||
.context("reading `/dev/attestation/quote`")?;
|
||||
|
||||
Ok(quote.into_boxed_slice())
|
||||
}
|
||||
|
||||
/// Wrapper func for error
|
||||
/// TODO: move to intel_tee_quote_verification_rs
|
||||
pub fn tee_qv_get_collateral(quote: &[u8]) -> Result<Collateral, Quote3Error> {
|
||||
intel_tee_quote_verification_rs::tee_qv_get_collateral(quote).map_err(Into::into)
|
||||
}
|
||||
|
|
30
crates/teepot/src/tdx/mod.rs
Normal file
30
crates/teepot/src/tdx/mod.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright (c) 2023-2024 Matter Labs
|
||||
|
||||
//! Intel TDX helper functions.
|
||||
|
||||
pub use crate::sgx::tcblevel::{parse_tcb_levels, EnumSet, TcbLevel};
|
||||
use crate::sgx::QuoteError;
|
||||
pub use intel_tee_quote_verification_rs::Collateral;
|
||||
use tdx_attest_rs::{tdx_att_get_quote, tdx_attest_error_t, tdx_report_data_t};
|
||||
|
||||
/// Get a TDX quote
|
||||
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] };
|
||||
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())
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,12 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (c) 2024 Matter Labs
|
||||
{ teepotCrate }: teepotCrate.craneLib.buildPackage (
|
||||
{ lib, pkgs, makeWrapper, teepotCrate }: teepotCrate.craneLib.buildPackage (
|
||||
teepotCrate.commonArgs // {
|
||||
pname = "teepot";
|
||||
inherit (teepotCrate) cargoArtifacts;
|
||||
|
||||
nativeBuildInputs = teepotCrate.commonArgs.nativeBuildInputs ++ [ makeWrapper ];
|
||||
|
||||
passthru = {
|
||||
inherit (teepotCrate) rustPlatform
|
||||
rustVersion
|
||||
|
@ -28,6 +30,7 @@
|
|||
"verify_attestation"
|
||||
"verify_era_proof_attestation"
|
||||
];
|
||||
|
||||
postInstall = ''
|
||||
removeReferencesToVendoredSources "$out" "$cargoVendorDir"
|
||||
removeReferencesToVendoredSources "$out" "${teepotCrate.rustVersion}/lib/rustlib/"
|
||||
|
@ -38,6 +41,11 @@
|
|||
echo -n "''${!i} " >> $out/nix-support/propagated-user-env-packages
|
||||
binname=''${i//_/-}
|
||||
mv "$out/bin/$binname" "''${!i}/bin/"
|
||||
|
||||
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 ]}" \
|
||||
--set-default QCNL_CONF_PATH "${pkgs.nixsgx.sgx-dcap.default_qpl}/etc/sgx_default_qcnl.conf"
|
||||
|
||||
done
|
||||
rmdir "$out/bin"
|
||||
'';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue