chore: split-out vault code from teepot in teepot-vault

Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
This commit is contained in:
Harald Hoyer 2025-02-18 13:37:34 +01:00
parent 63c16b1177
commit f8bd9e6a08
Signed by: harald
GPG key ID: F519A1143B3FBE32
61 changed files with 450 additions and 308 deletions

View file

@ -1,6 +1,6 @@
[package]
name = "teepot"
description = "TEE secret manager"
description = "TEE utilities"
# no MIT license, because of copied code from:
# * https://github.com/enarx/enarx
# * https://github.com/enarx/sgx
@ -11,18 +11,13 @@ authors.workspace = true
repository.workspace = true
[dependencies]
actix-http.workspace = true
actix-web.workspace = true
anyhow.workspace = true
async-trait.workspace = true
awc.workspace = true
bytemuck.workspace = true
bytes.workspace = true
clap.workspace = true
config.workspace = true
const-oid.workspace = true
enumset.workspace = true
futures-core.workspace = true
getrandom.workspace = true
hex.workspace = true
intel-tee-quote-verification-rs.workspace = true
@ -34,7 +29,6 @@ opentelemetry-otlp.workspace = true
opentelemetry-semantic-conventions.workspace = true
opentelemetry_sdk.workspace = true
p256.workspace = true
pgp.workspace = true
pkcs8.workspace = true
reqwest.workspace = true
rsa.workspace = true
@ -42,7 +36,6 @@ rustls.workspace = true
secp256k1 = { workspace = true, features = ["recovery"] }
serde.workspace = true
serde_json.workspace = true
serde_with.workspace = true
sha2.workspace = true
sha3.workspace = true
signature.workspace = true
@ -52,7 +45,6 @@ tracing.workspace = true
tracing-futures.workspace = true
tracing-log.workspace = true
tracing-subscriber.workspace = true
webpki-roots.workspace = true
x509-cert.workspace = true
zeroize.workspace = true
@ -61,4 +53,3 @@ base64.workspace = true
testaso.workspace = true
tokio.workspace = true
tracing-test.workspace = true
zksync_basic_types.workspace = true

View file

@ -1,312 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2023-2025 Matter Labs
//! Helper functions for CLI clients to verify Intel SGX enclaves and other TEEs.
#![deny(missing_docs)]
#![deny(clippy::all)]
pub mod vault;
use crate::{
quote::Report,
server::pki::{RaTlsCollateralExtension, RaTlsQuoteExtension},
sgx::Quote,
};
pub use crate::{
quote::{verify_quote_with_collateral, QuoteVerificationResult},
sgx::{parse_tcb_levels, sgx_ql_qv_result_t, EnumSet, TcbLevel},
};
use actix_web::http::header;
use anyhow::Result;
use awc::{Client, Connector};
use clap::Args;
use const_oid::AssociatedOid;
use intel_tee_quote_verification_rs::Collateral;
use rustls::{
client::{
danger::{HandshakeSignatureValid, ServerCertVerifier},
WebPkiServerVerifier,
},
pki_types::{CertificateDer, ServerName, UnixTime},
ClientConfig, DigitallySignedStruct, Error, SignatureScheme,
};
use sha2::{Digest, Sha256};
use std::{sync::Arc, time, time::Duration};
use tracing::{debug, error, info, trace, warn};
use x509_cert::{
der::{Decode as _, Encode as _},
Certificate,
};
/// Options and arguments needed to attest a TEE
#[derive(Args, Debug, Clone)]
pub struct AttestationArgs {
/// hex encoded SGX mrsigner of the enclave to attest
#[arg(long)]
pub sgx_mrsigner: Option<String>,
/// hex encoded SGX mrenclave of the enclave to attest
#[arg(long)]
pub sgx_mrenclave: Option<String>,
/// URL of the server
#[arg(long, required = true)]
pub server: String,
/// allowed TCB levels, comma separated:
/// Ok, ConfigNeeded, ConfigAndSwHardeningNeeded, SwHardeningNeeded, OutOfDate, OutOfDateConfigNeeded
#[arg(long, value_parser = parse_tcb_levels)]
pub sgx_allowed_tcb_levels: Option<EnumSet<TcbLevel>>,
}
/// A connection to a TEE, which implements the `teepot` attestation API
pub struct TeeConnection {
/// Options and arguments needed to attest a TEE
server: String,
client: Client,
}
impl TeeConnection {
/// Create a new connection to a TEE
///
/// This will verify the attestation report and check that the enclave
/// is running the expected code.
pub fn new(args: &AttestationArgs) -> Self {
let _ = rustls::crypto::ring::default_provider().install_default();
let tls_config = Arc::new(
ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(Arc::new(Self::make_verifier(args.clone())))
.with_no_client_auth(),
);
let agent = Client::builder()
.add_default_header((header::USER_AGENT, "teepot/1.0"))
// a "connector" wraps the stream into an encrypted connection
.connector(Connector::new().rustls_0_23(tls_config))
.timeout(Duration::from_secs(12000))
.finish();
Self {
server: args.server.clone(),
client: agent,
}
}
/// Create a new connection to a TEE
///
/// # Safety
/// This function is unsafe, because it does not verify the attestation report.
pub unsafe fn new_from_client_without_attestation(server: String, client: Client) -> Self {
Self { server, client }
}
/// Get a reference to the agent, which can be used to make requests to the TEE
///
/// Note, that it will refuse to connect to any other TLS server than the one
/// specified in the `AttestationArgs` of the `Self::new` function.
pub fn client(&self) -> &Client {
&self.client
}
/// Get a reference to the server URL
pub fn server(&self) -> &str {
&self.server
}
/// Save the hash of the public server key to `REPORT_DATA` to check
/// the attestations against it and it does not change on reconnect.
pub fn make_verifier(args: AttestationArgs) -> impl ServerCertVerifier {
#[derive(Debug)]
struct V {
args: AttestationArgs,
server_verifier: Arc<WebPkiServerVerifier>,
}
impl ServerCertVerifier for V {
fn verify_server_cert(
&self,
end_entity: &CertificateDer,
_intermediates: &[CertificateDer],
_server_name: &ServerName,
_ocsp_response: &[u8],
_now: UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
let cert = Certificate::from_der(end_entity.as_ref())
.map_err(|e| Error::General(format!("Failed get certificate {e:?}")))?;
let pub_key = cert
.tbs_certificate
.subject_public_key_info
.to_der()
.unwrap();
let hash = Sha256::digest(pub_key);
let exts = cert
.tbs_certificate
.extensions
.ok_or_else(|| Error::General("Failed get quote in certificate".into()))?;
trace!("Get quote bytes!");
let quote_bytes = exts
.iter()
.find(|ext| ext.extn_id == RaTlsQuoteExtension::OID)
.ok_or_else(|| Error::General("Failed get quote in certificate".into()))?
.extn_value
.as_bytes();
trace!("Get collateral bytes!");
let collateral = exts
.iter()
.find(|ext| ext.extn_id == RaTlsCollateralExtension::OID)
.and_then(|ext| {
serde_json::from_slice::<Collateral>(ext.extn_value.as_bytes())
.map_err(|e| {
debug!("Failed to get collateral in certificate {e:?}");
trace!(
"Failed to get collateral in certificate {:?}",
String::from_utf8_lossy(ext.extn_value.as_bytes())
);
})
.ok()
});
if collateral.is_none() {
debug!("Failed to get collateral in certificate");
}
let quote = Quote::try_from_bytes(quote_bytes).map_err(|e| {
Error::General(format!("Failed get quote in certificate {e:?}"))
})?;
if &quote.report_body.reportdata[..32] != hash.as_slice() {
error!("Report data mismatch");
return Err(Error::General("Report data mismatch".to_string()));
} else {
info!(
"Report data matches `{}`",
hex::encode(&quote.report_body.reportdata[..32])
);
}
let current_time: i64 = time::SystemTime::now()
.duration_since(time::UNIX_EPOCH)
.unwrap()
.as_secs() as _;
let QuoteVerificationResult {
collateral_expired,
result,
quote,
advisories,
earliest_expiration_date,
..
} = 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!(
"Collateral is out of date! Expired {}",
earliest_expiration_date
);
return Err(Error::General(format!(
"Collateral is out of date! Expired {}",
earliest_expiration_date
)));
}
let tcblevel = TcbLevel::from(result);
if self
.args
.sgx_allowed_tcb_levels
.map_or(true, |levels| !levels.contains(tcblevel))
{
error!("Quote verification result: {}", tcblevel);
return Err(Error::General(format!(
"Quote verification result: {}",
tcblevel
)));
}
info!("TcbLevel is allowed: {}", tcblevel);
}
for advisory in advisories {
warn!("Info: Advisory ID: {advisory}");
}
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 report_body.mr_signer[..] != mrsigner_bytes {
return Err(Error::General(format!(
"mrsigner mismatch: got {}, expected {}",
hex::encode(report_body.mr_signer),
&mrsigner
)));
} else {
info!("mrsigner `{mrsigner}` matches");
}
}
if let Some(mrenclave) = &self.args.sgx_mrenclave {
let mrenclave_bytes = hex::decode(mrenclave).map_err(|e| {
Error::General(format!("Failed to decode mrenclave: {}", e))
})?;
if report_body.mr_enclave[..] != mrenclave_bytes {
return Err(Error::General(format!(
"mrenclave mismatch: got {}, expected {}",
hex::encode(report_body.mr_enclave),
&mrenclave
)));
} else {
info!("mrenclave `{mrenclave}` matches");
}
}
info!("Quote verified! Connection secure!");
Ok(rustls::client::danger::ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> std::result::Result<HandshakeSignatureValid, Error> {
self.server_verifier
.verify_tls12_signature(message, cert, dss)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> std::result::Result<HandshakeSignatureValid, Error> {
self.server_verifier
.verify_tls13_signature(message, cert, dss)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.server_verifier.supported_verify_schemes()
}
}
let mut root_store = rustls::RootCertStore::empty();
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let server_verifier = WebPkiServerVerifier::builder(Arc::new(root_store))
.build()
.unwrap();
V {
args,
server_verifier,
}
}
}

View file

@ -1,376 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2023-2024 Matter Labs
//! Helper functions for CLI clients to verify Intel SGX enclaves and other TEEs.
#![deny(missing_docs)]
#![deny(clippy::all)]
use super::{AttestationArgs, TeeConnection};
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, ResponseError};
use anyhow::{anyhow, bail, Context, Result};
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},
sync::Arc,
time,
};
use tracing::{debug, error, info, trace};
const VAULT_TOKEN_HEADER: &str = "X-Vault-Token";
/// Error returned when sending a request to Vault
#[derive(Debug, thiserror::Error)]
pub enum VaultSendError {
/// Error sending the request
SendRequest(String),
/// Error returned by the Vault API
#[error(transparent)]
Vault(#[from] HttpResponseError),
}
impl Display for VaultSendError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
VaultSendError::SendRequest(e) => write!(f, "VaultSendError: {}", e),
VaultSendError::Vault(e) => write!(f, "VaultSendError: {}", e),
}
}
}
const _: () = {
fn assert_send<T: Send>() {}
let _ = assert_send::<VaultSendError>;
};
impl From<VaultSendError> for HttpResponseError {
fn from(value: VaultSendError) -> Self {
match value {
VaultSendError::SendRequest(e) => HttpResponseError::Anyhow(AnyHowResponseError {
status_code: StatusCode::BAD_GATEWAY,
error: anyhow!(e),
}),
VaultSendError::Vault(e) => e,
}
}
}
/// A connection to a Vault TEE, which implements the `teepot` attestation API
/// called by a TEE itself. This authenticates the TEE to Vault and gets a token,
/// which can be used to access the Vault API.
pub struct VaultConnection {
/// Options and arguments needed to attest Vault
pub conn: TeeConnection,
key_hash: [u8; 64],
client_token: String,
name: String,
}
impl VaultConnection {
/// Create a new connection to Vault
///
/// This will verify the attestation report and check that the enclave
/// is running the expected code.
pub async fn new(args: &AttestationArgs, name: String) -> Result<Self> {
let (key_hash, rustls_certificate, rustls_pk) =
make_self_signed_cert("CN=localhost", None)?;
let tls_config = Arc::new(
ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(Arc::new(TeeConnection::make_verifier(
args.clone(),
)))
.with_client_auth_cert(vec![rustls_certificate], rustls_pk)?,
);
let client = Client::builder()
.add_default_header((header::USER_AGENT, "teepot/1.0"))
// a "connector" wraps the stream into an encrypted connection
.connector(Connector::new().rustls_0_23(tls_config))
.timeout(time::Duration::from_secs(12000))
.finish();
let mut this = Self {
name,
key_hash,
conn: unsafe {
TeeConnection::new_from_client_without_attestation(args.server.clone(), client)
},
client_token: Default::default(),
};
this.client_token = this.auth().await?.auth.client_token;
trace!("Got Token: {:#?}", &this.client_token);
Ok(this)
}
/// create a new [`VaultConnection`] to Vault from an existing connection
///
/// # Safety
/// This function is unsafe, because it does not verify the attestation report.
pub unsafe fn new_from_client_without_attestation(
server: String,
client: Client,
name: String,
client_token: String,
) -> Self {
Self {
name,
client_token,
conn: unsafe { TeeConnection::new_from_client_without_attestation(server, client) },
key_hash: [0u8; 64],
}
}
/// Get a reference to the agent, which can be used to make requests to the TEE
///
/// Note, that it will refuse to connect to any other TLS server than the one
/// specified in the `AttestationArgs` of the `Self::new` function.
pub fn agent(&self) -> &Client {
self.conn.client()
}
async fn auth(&self) -> Result<AuthResponse> {
info!("Getting attestation report");
let attestation_url = AuthRequest::URL;
let quote = sgx_gramine_get_quote(&self.key_hash).context("Failed to get own quote")?;
let collateral = tee_qv_get_collateral(&quote).context("Failed to get own collateral")?;
let auth_req = AuthRequest {
name: self.name.clone(),
tee_type: "sgx".to_string(),
quote,
collateral: serde_json::to_string(&collateral)?,
challenge: None,
};
let mut response = self
.agent()
.post(&format!(
"{server}{attestation_url}",
server = self.conn.server,
))
.send_json(&auth_req)
.await
.map_err(|e| anyhow!("Error sending attestation request: {}", e))?;
let status_code = response.status();
if !status_code.is_success() {
error!("Failed to login to vault: {}", status_code);
if let Ok(r) = response.json::<Value>().await {
eprintln!("Failed to login to vault: {}", r);
}
bail!("failed to login to vault: {}", status_code);
}
let auth_response: Value = response.json().await.context("failed to login to vault")?;
trace!(
"Got AuthResponse: {:?}",
serde_json::to_string(&auth_response)
);
let auth_response: AuthResponse =
serde_json::from_value(auth_response).context("Failed to parse AuthResponse")?;
trace!("Got AuthResponse: {:#?}", &auth_response);
Ok(auth_response)
}
/// Send a put request to the vault
pub async fn vault_put(
&self,
action: &str,
url: &str,
json: &Value,
) -> std::result::Result<(StatusCode, Option<Value>), VaultSendError> {
let full_url = format!("{}{url}", self.conn.server);
info!("{action} via put {full_url}");
debug!(
"sending json: {:?}",
serde_json::to_string(json).unwrap_or_default()
);
let res = self
.agent()
.put(full_url)
.insert_header((VAULT_TOKEN_HEADER, self.client_token.clone()))
.send_json(json)
.await;
Self::handle_client_response(action, res).await
}
/// Send a get request to the vault
pub async fn vault_get(
&self,
action: &str,
url: &str,
) -> std::result::Result<(StatusCode, Option<Value>), VaultSendError> {
let full_url = format!("{}{url}", self.conn.server);
info!("{action} via get {full_url}");
let res = self
.agent()
.get(full_url)
.insert_header((VAULT_TOKEN_HEADER, self.client_token.clone()))
.send()
.await;
Self::handle_client_response(action, res).await
}
async fn handle_client_response<S>(
action: &str,
res: std::result::Result<ClientResponse<S>, SendRequestError>,
) -> std::result::Result<(StatusCode, Option<Value>), VaultSendError>
where
S: Stream<Item = Result<Bytes, PayloadError>>,
{
match res {
Ok(mut r) => {
let status_code = r.status();
if status_code.is_success() {
let msg = r.json().await.ok();
debug!(
"{action}: status code: {status_code} {:?}",
serde_json::to_string(&msg)
);
Ok((status_code, msg))
} else {
let err = HttpResponseError::from_proxy(r).await;
error!("{action}: {err:?}");
Err(err.into())
}
}
Err(e) => {
error!("{}: {}", action, e);
Err(VaultSendError::SendRequest(e.to_string()))
}
}
}
/// Revoke the token
pub async fn revoke_token(&self) -> std::result::Result<(), VaultSendError> {
self.vault_put(
"Revoke the token",
"/v1/auth/token/revoke-self",
&Value::default(),
)
.await?;
Ok(())
}
fn check_rel_path(rel_path: &str) -> Result<(), HttpResponseError> {
if !rel_path.is_ascii() {
return Err(anyhow!("path is not ascii")).status(StatusCode::BAD_REQUEST);
}
// check if rel_path is alphanumeric
if !rel_path
.chars()
.all(|c| c.is_alphanumeric() || c == '_' || c == '/')
{
return Err(anyhow!("path is not alphanumeric")).status(StatusCode::BAD_REQUEST);
}
Ok(())
}
/// set a secret in the vault
pub async fn store_secret<'de, T: serde::Serialize>(
&self,
val: T,
rel_path: &str,
) -> Result<(), HttpResponseError> {
self.store_secret_for_tee(&self.name, val, rel_path).await
}
/// set a secret in the vault for a different TEE
pub async fn store_secret_for_tee<'de, T: serde::Serialize>(
&self,
tee_name: &str,
val: T,
rel_path: &str,
) -> Result<(), HttpResponseError> {
Self::check_rel_path(rel_path)?;
let value = serde_json::to_value(val)
.context("converting value to json")
.status(StatusCode::INTERNAL_SERVER_ERROR)?;
let value = json!({ "data" : { "_" : value } });
self.vault_put(
"Setting secret",
&format!("/v1/secret/data/tee/{}/{}", tee_name, rel_path),
&value,
)
.await?;
Ok(())
}
/// get a secret from the vault
pub async fn load_secret<'de, T: serde::de::DeserializeOwned>(
&self,
rel_path: &str,
) -> Result<Option<T>, HttpResponseError> {
self.load_secret_for_tee(&self.name, rel_path).await
}
/// get a secret from the vault for a specific TEE
pub async fn load_secret_for_tee<'de, T: serde::de::DeserializeOwned>(
&self,
tee_name: &str,
rel_path: &str,
) -> Result<Option<T>, HttpResponseError> {
Self::check_rel_path(rel_path)?;
let v = self
.vault_get(
"Getting secret",
&format!("/v1/secret/data/tee/{}/{}", tee_name, rel_path),
)
.await
.or_else(|e| match e {
VaultSendError::Vault(ref se) => {
if se.status_code() == StatusCode::NOT_FOUND {
debug!("Secret not found: {}", rel_path);
Ok((StatusCode::OK, None))
} else {
Err(e)
}
}
VaultSendError::SendRequest(_) => Err(e),
})?
.1
.as_ref()
.and_then(|v| v.get("data"))
.and_then(|v| v.get("data"))
.and_then(|v| v.get("_"))
.and_then(|v| serde_json::from_value(v.clone()).transpose())
.transpose()
.context("Error getting value from vault")
.status(StatusCode::INTERNAL_SERVER_ERROR)?
.flatten();
Ok(v)
}
}

View file

@ -37,7 +37,6 @@ pub fn public_key_to_ethereum_address(public: &PublicKey) -> [u8; 20] {
#[cfg(test)]
mod tests {
use secp256k1::{Secp256k1, SecretKey};
use zksync_basic_types::H256;
use super::*;
@ -71,10 +70,10 @@ mod tests {
assert_eq!(address, expected_address.as_slice());
// Generate a random root hash, create a message from the hash, and sign the message using
// Take a root hash, create a message from the hash, and sign the message using
// the secret key
let root_hash = H256::random();
let root_hash_bytes = root_hash.as_bytes();
let root_hash = b"12345678901234567890123456789012";
let root_hash_bytes = root_hash.as_slice();
let msg_to_sign = Message::from_digest(root_hash_bytes.try_into().unwrap());
let signature = sign_message(&secret_key, msg_to_sign).unwrap();

View file

@ -1,277 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2023-2024 Matter Labs
//! Common types for the teepot http JSON API
use crate::sgx::Collateral;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_with::base64::Base64;
use serde_with::serde_as;
use std::fmt::Display;
use std::sync::Arc;
/// The unseal request data
#[derive(Debug, Serialize, Deserialize)]
pub struct Unseal {
/// The unseal key
pub key: String,
}
impl Unseal {
/// The unseal URL
pub const URL: &'static str = "/v1/sys/unseal";
}
/// The attestation response
#[derive(Debug, Serialize, Deserialize)]
pub struct AttestationResponse {
/// The quote
pub quote: Arc<[u8]>,
/// The collateral
pub collateral: Arc<Collateral>,
}
/// The init request data
#[serde_as]
#[derive(Debug, Serialize, Deserialize)]
pub struct Init {
/// PGP keys to encrypt the unseal keys with
pub pgp_keys: Vec<String>,
/// number of secret shares
pub secret_shares: usize,
/// secret threshold
pub secret_threshold: usize,
/// PGP keys to sign commands for the admin tee
#[serde_as(as = "Box<[Base64]>")]
pub admin_pgp_keys: Box<[Box<[u8]>]>,
/// admin threshold
pub admin_threshold: usize,
/// admin TEE mrenclave
pub admin_tee_mrenclave: String,
}
impl Init {
/// The init URL
pub const URL: &'static str = "/v1/sys/init";
}
/// The init request data
#[derive(Debug, Serialize, Deserialize)]
pub struct VaultInitRequest {
/// PGP keys to encrypt the unseal keys with
pub pgp_keys: Vec<String>,
/// number of secret shares
pub secret_shares: usize,
/// secret threshold
pub secret_threshold: usize,
}
/// The init response data
#[derive(Debug, Serialize, Deserialize)]
pub struct InitResponse {
/// The unseal keys (gpg encrypted)
pub unseal_keys: Vec<String>,
}
/// The Vault TEE auth request data
#[serde_as]
#[derive(Debug, Serialize, Deserialize)]
pub struct AuthRequest {
/// The name of the TEE
pub name: String,
/// The type of the TEE
#[serde(rename = "type")]
pub tee_type: String,
/// The attestation report data base64 encoded
#[serde_as(as = "Base64")]
pub quote: Box<[u8]>,
/// The attestation collateral json encoded
pub collateral: String,
/// The vault attestation challenge (hex encoded)
#[serde_as(as = "Option<serde_with::hex::Hex>")]
#[serde(skip_serializing_if = "Option::is_none", default = "Option::default")]
pub challenge: Option<[u8; 32]>,
}
impl AuthRequest {
/// The auth URL
pub const URL: &'static str = "/v1/auth/tee/login";
}
/// Vault auth metadata
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct AuthMetadataField {
collateral_expiration_date: String,
tee_name: String,
}
/// Vault auth data
#[serde_as]
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct AuthDataField {
/// The attestation report data base64 encoded
#[serde_as(as = "Base64")]
#[serde(default)]
pub quote: Box<[u8]>,
/// The attestation collateral json encoded
#[serde(default)]
pub collateral: String,
}
/// Vault auth
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct AuthField {
/// if the auth token is renewable
pub renewable: bool,
/// the lease duration of the auth token
pub lease_duration: isize,
/// the policies of the auth token
pub policies: Vec<String>,
/// the accessor of the auth token
pub accessor: String,
/// the client token
pub client_token: String,
/// additional metadata
pub metadata: AuthMetadataField,
}
/// The Vault TEE auth response data
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct AuthResponse {
/// vault auth
pub auth: AuthField,
/// vault auth data
pub data: AuthDataField,
}
/// One command datum
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct VaultCommand {
/// The command to execute
pub url: String,
/// The command to execute
pub data: Value,
}
impl Display for VaultCommand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
f.write_str(
serde_json::to_string_pretty(self)
.unwrap_or("{}".into())
.as_str(),
)
} else {
f.write_str(serde_json::to_string(self).unwrap_or("{}".into()).as_str())
}
}
}
/// Multiple command data
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct VaultCommands {
/// The sha-256 hash of the last command hex encoded
pub last_digest: String,
/// The actual commands
pub commands: Vec<VaultCommand>,
}
/// The command request data
#[derive(Debug, Serialize, Deserialize)]
pub struct VaultCommandRequest {
/// The commands to execute
///
/// The commands are json serialized `VaultCommands`,
/// because they are signed with multiple signatures.
///
/// The commands are executed in order.
pub commands: String,
/// The signatures of the commands
pub signatures: Vec<String>,
}
impl VaultCommandRequest {
/// The command request URL
pub const URL: &'static str = "/v1/command";
}
/// The command response
#[derive(Debug, Serialize, Deserialize)]
pub struct VaultCommandResponse {
/// The status code
pub status_code: u16,
/// The response body
pub value: Option<Value>,
}
/// The command response
#[derive(Debug, Serialize, Deserialize)]
pub struct VaultCommandsResponse {
/// The stored digest for the execution
pub digest: String,
/// The results of the individual commands
pub results: Vec<VaultCommandResponse>,
}
impl Display for VaultCommandResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
f.write_str(
serde_json::to_string_pretty(self)
.unwrap_or("{}".into())
.as_str(),
)
} else {
f.write_str(serde_json::to_string(self).unwrap_or("{}".into()).as_str())
}
}
}
/// The command request URL
pub const DIGEST_URL: &str = "/v1/digest";
/// The signing request
#[derive(Debug, Serialize, Deserialize)]
pub struct SignRequest {
/// json serialized `SignRequestData`, because it is signed with multiple signatures.
pub sign_request_data: String,
/// The signatures of the SignRequestData
pub signatures: Vec<String>,
}
impl SignRequest {
/// The sign request URL
pub const URL: &'static str = "/v1/sign";
}
/// The signing request data
#[serde_as]
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct SignRequestData {
/// The sha-256 hash of the last command hex encoded
pub last_digest: String,
/// The name of the TEE
pub tee_name: String,
/// The type of the TEE
#[serde(rename = "type")]
pub tee_type: String,
/// The TEE security version number
pub tee_svn: u16,
/// The data to be signed.
///
/// In case of `tee_type == "sgx"`, it's the SGX Sigstruct Body
#[serde_as(as = "Base64")]
pub data: Vec<u8>,
}
/// The signing request
#[derive(Debug, Serialize, Deserialize)]
pub struct SignResponse {
/// The stored digest for the execution
pub digest: String,
/// The signed data for the tee.
///
/// In case of `tee_type == "sgx"`, it's the SGX Sigstruct
pub signed_data: Vec<u8>,
}

View file

@ -1,10 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2023 Matter Labs
//! Common types for the teepot JSON API
#![deny(missing_docs)]
#![deny(clippy::all)]
pub mod http;
pub mod secrets;

View file

@ -1,34 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2023 Matter Labs
//! Common types for the teepot secrets JSON API
use crate::sgx::sign::Zeroizing;
use serde::{Deserialize, Serialize};
use serde_with::base64::Base64;
use serde_with::serde_as;
/// Configuration for the admin tee
#[serde_as]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AdminConfig {
/// PGP keys to sign commands for the admin tee
#[serde_as(as = "Box<[Base64]>")]
pub admin_pgp_keys: Box<[Box<[u8]>]>,
/// admin threshold
pub admin_threshold: usize,
}
/// Configuration for the admin tee
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct AdminState {
/// last digest of executed commands
pub last_digest: String,
}
/// Configuration for the admin tee
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct SGXSigningKey {
/// private key in PEM format
pub pem_pk: Zeroizing<String>,
}

View file

@ -6,14 +6,12 @@
#![deny(missing_docs)]
#![deny(clippy::all)]
pub mod client;
pub mod config;
pub mod ethereum;
pub mod json;
pub mod log;
pub mod pki;
pub mod prover;
pub mod quote;
pub mod server;
pub mod sgx;
pub mod tdx;

View file

@ -1,18 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2023-2024 Matter Labs
// Copyright (c) 2023-2025 Matter Labs
//! Common attestation API for all TEEs
use crate::{
client::AttestationArgs,
json::http::AttestationResponse,
quote::{
error::QuoteContext, get_quote, verify_quote_with_collateral, QuoteVerificationResult,
},
sgx::{parse_tcb_levels, Collateral, EnumSet, TcbLevel},
sgx::{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::{
@ -28,6 +25,15 @@ struct Attestation {
earliest_expiration_date: i64,
}
/// The attestation response
#[derive(Debug, Serialize, Deserialize)]
pub struct AttestationResponse {
/// The quote
pub quote: Arc<[u8]>,
/// The collateral
pub collateral: Arc<Collateral>,
}
/// Returns the quote and collateral for the current TEE.
///
/// if `allowed_tcb_levels` is `None`, then any TCB level is accepted.
@ -110,43 +116,3 @@ pub fn get_quote_and_collateral(
Ok(AttestationResponse { quote, collateral })
}
/// Options and arguments needed to attest a TEE
#[derive(Args, Debug, Clone, Serialize, Deserialize, Default)]
pub struct VaultAttestationArgs {
/// hex encoded SGX mrsigner of the enclave to attest
#[arg(long, env = "VAULT_SGX_MRSIGNER")]
pub vault_sgx_mrsigner: Option<String>,
/// hex encoded SGX mrenclave of the enclave to attest
#[arg(long, env = "VAULT_SGX_MRENCLAVE")]
pub vault_sgx_mrenclave: Option<String>,
/// URL of the server
#[arg(long, required = true, env = "VAULT_ADDR")]
pub vault_addr: String,
/// allowed TCB levels, comma separated:
/// Ok, ConfigNeeded, ConfigAndSwHardeningNeeded, SwHardeningNeeded, OutOfDate, OutOfDateConfigNeeded
#[arg(long, value_parser = parse_tcb_levels, env = "VAULT_SGX_ALLOWED_TCB_LEVELS")]
pub vault_sgx_allowed_tcb_levels: Option<EnumSet<TcbLevel>>,
}
impl From<VaultAttestationArgs> for AttestationArgs {
fn from(value: VaultAttestationArgs) -> Self {
AttestationArgs {
sgx_mrsigner: value.vault_sgx_mrsigner,
sgx_mrenclave: value.vault_sgx_mrenclave,
server: value.vault_addr,
sgx_allowed_tcb_levels: value.vault_sgx_allowed_tcb_levels,
}
}
}
impl From<&VaultAttestationArgs> for AttestationArgs {
fn from(value: &VaultAttestationArgs) -> Self {
AttestationArgs {
sgx_mrsigner: value.vault_sgx_mrsigner.clone(),
sgx_mrenclave: value.vault_sgx_mrenclave.clone(),
server: value.vault_addr.clone(),
sgx_allowed_tcb_levels: value.vault_sgx_allowed_tcb_levels,
}
}
}

View file

@ -1,10 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2023-2024 Matter Labs
// Copyright (c) 2023-2025 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 attestation;
pub mod error;
use crate::{

View file

@ -1,187 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2023-2024 Matter Labs
//! # tee-server
#![deny(missing_docs)]
#![deny(clippy::all)]
pub mod attestation;
pub mod pki;
pub mod signatures;
use actix_web::http::StatusCode;
use actix_web::web::Bytes;
use actix_web::{error, HttpRequest, HttpResponse};
use actix_web::{HttpMessage, ResponseError};
use anyhow::anyhow;
use awc::error::{PayloadError, SendRequestError};
use awc::ClientResponse;
use futures_core::Stream;
use std::fmt::{Debug, Display, Formatter};
use tracing::error;
/// Anyhow error with an HTTP status code
pub struct AnyHowResponseError {
/// error message
pub error: anyhow::Error,
/// HTTP status code
pub status_code: StatusCode,
}
/// Proxy response error
pub struct ProxyResponseError {
/// HTTP status code
pub status_code: StatusCode,
/// HTTP body
pub body: Option<Bytes>,
/// HTTP content type
pub content_type: String,
}
/// custom HTTP response error
pub enum HttpResponseError {
/// Anyhow error
Anyhow(AnyHowResponseError),
/// Proxy error
Proxy(ProxyResponseError),
}
impl std::error::Error for HttpResponseError {}
/// Attach an HTTP status code to an anyhow error turning it into an HttpResponseError
pub trait Status {
/// The Ok type
type Ok;
/// Attach an HTTP status code to an anyhow error turning it into an HttpResponseError
fn status(self, status: StatusCode) -> Result<Self::Ok, HttpResponseError>;
}
impl<T> Status for Result<T, anyhow::Error> {
type Ok = T;
fn status(self, status: StatusCode) -> Result<T, HttpResponseError> {
match self {
Ok(value) => Ok(value),
Err(error) => Err(HttpResponseError::new(error, status)),
}
}
}
impl HttpResponseError {
fn new(error: anyhow::Error, status_code: StatusCode) -> Self {
Self::Anyhow(AnyHowResponseError { error, status_code })
}
/// Create a new HTTP response error from a proxy response
pub async fn from_proxy<S>(mut response: ClientResponse<S>) -> Self
where
S: Stream<Item = Result<Bytes, PayloadError>>,
{
let status_code = response.status();
let body = response.body().await.ok();
let content_type = response.content_type().to_string();
error!(
"Vault returned server error: {status_code} {}",
body.as_ref()
.map_or("", |b| std::str::from_utf8(b).unwrap_or(""))
);
Self::Proxy(ProxyResponseError {
status_code,
body,
content_type,
})
}
}
impl From<&str> for HttpResponseError {
fn from(value: &str) -> Self {
error!("{}", value);
HttpResponseError::new(
anyhow!(value.to_string()),
StatusCode::INTERNAL_SERVER_ERROR,
)
}
}
impl From<SendRequestError> for HttpResponseError {
fn from(error: SendRequestError) -> Self {
error!("Error sending request: {:?}", error);
HttpResponseError::new(
anyhow!(error.to_string()),
StatusCode::INTERNAL_SERVER_ERROR,
)
}
}
impl Debug for HttpResponseError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let Self::Anyhow(e) = self {
if f.alternate() {
write!(f, "{:#?}", e.error)
} else {
write!(f, "{:?}", e.error)
}
} else {
write!(f, "HttpResponseError")
}
}
}
impl Display for HttpResponseError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let Self::Anyhow(e) = self {
if f.alternate() {
write!(f, "{:#}", e.error)
} else {
write!(f, "{}", e.error)
}
} else {
write!(f, "HttpResponseError")
}
}
}
impl ResponseError for HttpResponseError {
fn status_code(&self) -> StatusCode {
match self {
HttpResponseError::Anyhow(e) => e.status_code,
HttpResponseError::Proxy(e) => e.status_code,
}
}
fn error_response(&self) -> HttpResponse {
match self {
HttpResponseError::Anyhow(e) => HttpResponse::build(self.status_code())
.content_type("application/json")
.body(format!(r#"{{"error":"{}"}}"#, e.error)),
HttpResponseError::Proxy(e) => {
if let Some(ref body) = e.body {
HttpResponse::build(self.status_code())
.content_type(e.content_type.clone())
.body(body.clone())
} else {
HttpResponse::new(self.status_code())
}
}
}
}
}
/// Create a new json config
pub fn new_json_cfg() -> actix_web::web::JsonConfig {
actix_web::web::JsonConfig::default()
.limit(1024 * 1024)
.error_handler(json_error_handler)
}
fn json_error_handler(err: error::JsonPayloadError, _: &HttpRequest) -> actix_web::Error {
error::InternalError::from_response(
"",
HttpResponse::BadRequest()
.content_type("application/json")
.body(format!(r#"{{"error":"json error: {}"}}"#, err)),
)
.into()
}

View file

@ -1,120 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2023-2024 Matter Labs
//! Signature checking utilities
use crate::json::secrets::AdminConfig;
use crate::server::{HttpResponseError, Status as _};
use actix_web::http::StatusCode;
use anyhow::{anyhow, bail, Context, Result};
use pgp::types::PublicKeyTrait;
use pgp::{Deserializable, SignedPublicKey, StandaloneSignature};
use tracing::debug;
/// Verify a pgp signature for some message given some public keys
pub fn verify_sig(sig: &str, msg: &[u8], keys: &[SignedPublicKey]) -> anyhow::Result<usize> {
let (signatures, _) =
StandaloneSignature::from_string_many(sig).context(format!("reading signature {}", sig))?;
for signature in signatures {
let signature = match signature {
Ok(s) => s,
Err(e) => {
debug!("Failed to parse signature: {}", e);
continue;
}
};
for (pos, key) in keys.iter().enumerate() {
let actual_key = &key.primary_key;
if actual_key.is_signing_key() && signature.verify(&actual_key, msg).is_ok() {
return Ok(pos);
}
for sub_key in &key.public_subkeys {
if sub_key.is_signing_key() && signature.verify(sub_key, msg).is_ok() {
return Ok(pos);
}
}
}
}
eprintln!("Failed to verify signature for `{sig}`");
bail!("Failed to verify signature for `{sig}`");
}
/// Verify pgp signatures for a message with some threshold
pub fn check_sigs(
pgp_keys: &[Box<[u8]>],
threshold: usize,
signatures: &[String],
msg: &[u8],
) -> Result<(), HttpResponseError> {
let mut keys = Vec::new();
for bytes in pgp_keys {
let key = SignedPublicKey::from_bytes(bytes.as_ref())
.context("parsing public key")
.status(StatusCode::INTERNAL_SERVER_ERROR)?;
keys.push(key);
}
let mut verified: usize = 0;
for sig in signatures {
if let Ok(pos) = verify_sig(sig, msg, &keys) {
keys.remove(pos);
verified += 1;
}
if verified >= threshold {
break;
}
}
if verified < threshold {
return Err(anyhow!("not enough valid signatures")).status(StatusCode::BAD_REQUEST);
}
Ok(())
}
/// Verify pgp signatures for a message
pub trait VerifySig {
/// Verify pgp signatures for a message
fn check_sigs(&self, signatures: &[String], msg: &[u8]) -> Result<(), HttpResponseError>;
}
impl VerifySig for AdminConfig {
fn check_sigs(&self, signatures: &[String], msg: &[u8]) -> Result<(), HttpResponseError> {
check_sigs(&self.admin_pgp_keys, self.admin_threshold, signatures, msg)
}
}
#[cfg(test)]
mod tests {
use super::verify_sig;
use base64::{engine::general_purpose, Engine as _};
use pgp::{Deserializable, SignedPublicKey};
const TEST_DATA: &str = include_str!("../../tests/data/test.json");
// gpg --armor --local-user test@example.com --detach-sign bin/tee-vault-admin/tests/data/test.json
const TEST_SIG: &str = include_str!("../../tests/data/test.json.asc");
// gpg --armor --export 81A312C59D679D930FA9E8B06D728F29A2DBABF8 > bin/tee-vault-admin/tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.asc
const TEST_KEY: &str =
include_str!("../../tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.asc");
const TEST_KEY_BASE64: &str =
include_str!("../../tests/data/pub-81A312C59D679D930FA9E8B06D728F29A2DBABF8.b64");
#[test]
fn test_sig() {
let test_key = SignedPublicKey::from_string(TEST_KEY).unwrap().0;
verify_sig(TEST_SIG, TEST_DATA.as_bytes(), &[test_key]).unwrap();
}
#[test]
fn test_key_import() {
let str = TEST_KEY_BASE64.lines().collect::<String>();
let bytes = general_purpose::STANDARD.decode(str).unwrap();
let _ = SignedPublicKey::from_bytes(bytes.as_slice()).unwrap();
}
}

View file

@ -1,83 +0,0 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
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
NcPkByuqaJvurs3VRpU4rOyFaxaTKXfr
=Bpm9
-----END PGP PRIVATE KEY BLOCK-----

View file

@ -1,31 +0,0 @@
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
NcPkByuqaJvurs3VRpU4rOyFaxaTKXfr

View file

@ -1,23 +0,0 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQGNBGTBKKQBDACo28S5h7rvfcj89OIv66evSsOExJJ363oYWBfs0GPkgPG/bS2p
gIFe5+yqvW5q2tnDYHz0WJkYQcwTqQQpt/Ma+UV+uaHc2pJfKvsmpI9o3xf+eV3C
9HrU+6lwr8efkBkjLrfzkroQf0II/eX9omePt4qXNMX07UKI1ZrmXWmn5BlXiwkQ
l0M9XWwS3PZ2aiM2MMUjfmDAdi5pc10UgZddlbInjK8guXj9/HQt23l3W1df83QN
lxC+sDmlekwugeg0HckcgTjGICvML+gwsX/GVDMJe/O8D7Sy7KKrO23xwus7p8At
4C5+0WCqARBKQbTE0IZTyUZUgt6xnn1BhFHBCblpI2KPkRXSUWBkb/Npr40Y2uMi
BZNk2Iwdyuim8+Zs5pHCISvR09COswFgM+K7ikv7EMMr1YsEcFGH4sE3GQXVw4qO
NdITNBAz/5+cPWAOUOske6BeUjLaC4XaG4wY2Rg7RVoDCqhKav+VYE2B/X0pNVSf
PyZKirkMllHVwA8AEQEAAbQXdGVzdCA8dGVzdEBleGFtcGxlLmNvbT6JAdEEEwEI
ADsWIQSBoxLFnWedkw+p6LBtco8potur+AUCZMEopAIbAwULCQgHAgIiAgYVCgkI
CwIEFgIDAQIeBwIXgAAKCRBtco8potur+Cb2C/9sc2VYpi8eYnPrclU/28SoecG9
cyPAZ5D+AQfAnVPLDdeBgLLr4FrMkRLvFNEoNwSFWAHNde77ChKFVPjqs1ubR+P6
RJ8YjeuF5l5objn/xXd+fe79gjBfWRVUR36+q9TvJwr51bL/cFNv0eGpvxV4OhiX
ImF99Ik3HtXX2w5b8rkH2DcW39HlQvxqOY3OTskmVn0nv5l6Q9CHrIXsaug11Bp/
EQPyMG/E0Qa1SWCIyDwphEQzIUFIBg0zlAUC2Tlh9PYrgZJ5GjNY18a0ZglwTp48
IpwOUXwT9IzOKyrM1jA9lg8P5ih/MLZKIMvroKrry+IZXQAujnvH0HwMehDuepR+
liOx1byI2VitOkl7GpAZ03t+ae/KrsjJ+Dx/oFFkDVQAF2dDJHiFzZD8DHucIngY
4b8Wq7N+8hzyk8AgKFK9rq3HqPH5L+NsJEzS1xOQeaGlI1H1knnAgRuIBZdZewrR
/UosYei3mZtEclHU+YqeeBAsVoKKSVi2ryu8RCk=
=LKzc
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -1,16 +0,0 @@
mQGNBGTBKKQBDACo28S5h7rvfcj89OIv66evSsOExJJ363oYWBfs0GPkgPG/bS2pgIFe5+yqvW5q
2tnDYHz0WJkYQcwTqQQpt/Ma+UV+uaHc2pJfKvsmpI9o3xf+eV3C9HrU+6lwr8efkBkjLrfzkroQ
f0II/eX9omePt4qXNMX07UKI1ZrmXWmn5BlXiwkQl0M9XWwS3PZ2aiM2MMUjfmDAdi5pc10UgZdd
lbInjK8guXj9/HQt23l3W1df83QNlxC+sDmlekwugeg0HckcgTjGICvML+gwsX/GVDMJe/O8D7Sy
7KKrO23xwus7p8At4C5+0WCqARBKQbTE0IZTyUZUgt6xnn1BhFHBCblpI2KPkRXSUWBkb/Npr40Y
2uMiBZNk2Iwdyuim8+Zs5pHCISvR09COswFgM+K7ikv7EMMr1YsEcFGH4sE3GQXVw4qONdITNBAz
/5+cPWAOUOske6BeUjLaC4XaG4wY2Rg7RVoDCqhKav+VYE2B/X0pNVSfPyZKirkMllHVwA8AEQEA
AbQXdGVzdCA8dGVzdEBleGFtcGxlLmNvbT6JAdEEEwEIADsWIQSBoxLFnWedkw+p6LBtco8potur
+AUCZMEopAIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRBtco8potur+Cb2C/9sc2VY
pi8eYnPrclU/28SoecG9cyPAZ5D+AQfAnVPLDdeBgLLr4FrMkRLvFNEoNwSFWAHNde77ChKFVPjq
s1ubR+P6RJ8YjeuF5l5objn/xXd+fe79gjBfWRVUR36+q9TvJwr51bL/cFNv0eGpvxV4OhiXImF9
9Ik3HtXX2w5b8rkH2DcW39HlQvxqOY3OTskmVn0nv5l6Q9CHrIXsaug11Bp/EQPyMG/E0Qa1SWCI
yDwphEQzIUFIBg0zlAUC2Tlh9PYrgZJ5GjNY18a0ZglwTp48IpwOUXwT9IzOKyrM1jA9lg8P5ih/
MLZKIMvroKrry+IZXQAujnvH0HwMehDuepR+liOx1byI2VitOkl7GpAZ03t+ae/KrsjJ+Dx/oFFk
DVQAF2dDJHiFzZD8DHucIngY4b8Wq7N+8hzyk8AgKFK9rq3HqPH5L+NsJEzS1xOQeaGlI1H1knnA
gRuIBZdZewrR/UosYei3mZtEclHU+YqeeBAsVoKKSVi2ryu8RCk=

View file

@ -1,22 +0,0 @@
{
"last_digest": "",
"commands": [
{
"url": "/v1/sys/policies/acl/tee-stress",
"data": {
"policy": "path \"secret/data/tee/stress/*\" { capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\" ] }\n"
}
},
{
"url": "/v1/auth/tee/tees/stress",
"data": {
"lease": "1000",
"name": "stress",
"sgx_allowed_tcb_levels": "Ok,SwHardeningNeeded",
"sgx_mrsigner": "c5591a72b8b86e0d8814d6e8750e3efe66aea2d102b8ba2405365559b858697d",
"token_policies": "tee-stress",
"types": "sgx"
}
}
]
}

View file

@ -1,26 +0,0 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEC0Pk344t4+guABNk9RmhFDs/vjIFAmUn5WkACgkQ9RmhFDs/
vjK5kw/8Dl1XuMOfJ+mxshaRH4JexPmB/+c4x5JnaYS6GGJaJ/eOp+hSFchxnwfI
OXRV3S+/kgaytfu5zVEepqyypX43LbV+eZ5s450xa3qI0fR/rd+LnJeJNqFoJVrZ
M+xTGpkBPvPB3340ebyti5k49I+uhgO8c5Cd1FpM00dWN0qUUJMsFuOKNueFURlC
Sk9HLDk63G2/ZDyBzT83vMpFUZbtJ46yJmS2++W1UfEt2GZvL6sc2wr7pwlb5EtF
wEgLtaIAy749h1Lzilw6RTWVC3ShQvaddIFIh+XagnrKmk2D0gha2XprbvBSkpYf
a/9tZKH/4U8wfONR3sJ83wlODwks6zdIibspk7868vY5Bm9Yr2N1cIIPGtnnypHE
xPZI9QXY/zUSnTXgs5JyEZkea8j29v135zhuGINFPVWOa+frGYTIN/zZ3sjdhZMt
+4rRL8SZbFbkCDc/zGdlJOcTygUbEJBiseNJ8GXgbWrXzY/WDZpxL1xdxjkPK4PA
xtKyaPlBP1B1RyHkZGYDq86t9DzX2H/gkqBHJpuSavcx/7/Q/b6KGYdf/QFxo7kY
S0jdVXRVem0ClxWtEVZU9Wu9QykcYQbj3AM7hk+9Khmq32w7b3bwOndYNAojwzP+
9UEVOAXv2K8LSBbq6RXott5KMKDwowOu4hQCNsDuvmBYkr1Sy5OJAcUEAAEIAC8W
IQSBoxLFnWedkw+p6LBtco8potur+AUCZSflbREcdGVzdEBleGFtcGxlLmNvbQAK
CRBtco8potur+LwPC/wOjT27sE4D/4Cadg58lXlRE2qoFdtc8vfs+ioxS7UxQX4m
ggY4P6lHq8u2TkY4jDe9FpA5S7LNGQJoQx2zrr3lGwonBwGkj4nRM60/uNSar+wd
Wknke8IUiv8E8MzITy+gKdFHwu95ZZh9IXefiQ4Fq8UQurELAfVA/sNk+1ovGzsO
/S4srkR4uejsAuk84PCA7dgNLYobcU/7SMH/ffgorqE6BOXwzfIy13c9TV5ZztWo
eK6R+wc92hza0ZvXVmB4i5NBe+aO7gSLe0QcJqHdaTpkcVhhhE+v8HdpF1JIgOH8
/336W/ZOp1q1K0hL2rNU2YX40MOaZZLoxjfXNmC/dAZPel5HJMwTLzM6Aqqk49sB
LHEPgHjefUWiHe2C31PGM0THM3fuA6i5OwypnZRI14WYVDlVa5KRmj/titcCt6aQ
+fbzK5lYIg4AhLl8rIns8+/yJnwTIw3Zy94H8Xwjq8tplk6nSUWm0GKjYqFquwUf
6PyKGVnqs2Cp0hFmD9o=
=AsWI
-----END PGP SIGNATURE-----

View file

@ -1,16 +0,0 @@
{
"last_digest": "",
"commands": [
{
"url": "/v1/auth/tee/tees/test",
"data": {
"lease": "1000",
"name": "test",
"sgx_allowed_tcb_levels": "Ok,SwHardeningNeeded",
"sgx_mrsigner": "c5591a72b8b86e0d8814d6e8750e3efe66aea2d102b8ba2405365559b858697d",
"token_policies": "test",
"types": "sgx"
}
}
]
}

View file

@ -1,26 +0,0 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEC0Pk344t4+guABNk9RmhFDs/vjIFAmT5sBwACgkQ9RmhFDs/
vjIJ2g/9E8kRdvz8hhxOyJRPpNZ9bcGJ+FvMjG7geEdixqu1Bwpfyj+UhGRY7Dgq
4T46w/Hmr8YBZTI3xpafUSldyEvZnFXRuIQoBR+JQYNu8s9Jm9yDIyLLA86quiY8
nU+x6x89sSOOvmTpRUBi6htTC4h0zZHHfAcmu0YS2pMmxXYJX7Rw3T7AJE0Uc4O/
+0Ho1PMFB4BRmnNGlqBFc2u/yXEy38AWrmcheoPNtbdWmI/3leKikW5l2cXfPRuT
tazc92NiIK6qu209jlusMBpADdu8FSAI9ax4dKL1uE8KQyUIKQVQq3sBqQsPTgiW
XT6XznFdWawtW1y2jzt0DbdCt2osSwV7rPYbn6yxEpzQWuPL75JiDmJMktgBV38t
FKBpQl1ZDF9wARwsxBvNRaL0XPSurTtf/x4olue91D3I2fNeymS6P1DNXvLGXPD5
46C2TvS1305wPjCpFAEgS58WpiiJppCYDsU0A7DEHbaIgk5mQ/iEdBOBvrn0xton
Rni5mXt0Vi0WfE8GriR25YajtAOI2/rxTSZjiSJI51WmdnV/lkJiY1YF0ws+KUXG
r2E+Bea0kwcnCMYFzraxwLwiS4mgamdQmp8DNALYZe8m/k+dNKI6tDEdWFMTiwda
PJMDc3+lUOcZCN6+umLUOVvNWQZ2QtZ4RSTzbJyDww4ysgTpFHOJAcUEAAEIAC8W
IQSBoxLFnWedkw+p6LBtco8potur+AUCZPmwIREcdGVzdEBleGFtcGxlLmNvbQAK
CRBtco8potur+CMVDACm+OMPck8lmOnNGphP7kKtMcyMyIvsI2Ik/fB3A2z+iXnX
TQljubYTqN4Hzv5MECoUhm3GiWJOYjGoXnpec1zAk4VAewin1814Dldq/9CQ+YlN
W7HKjCiCBSdk2tPEBUK6gfV+OU9N2mNp/+biuwNTVHpPokIiwBE3MKjtUc0W0xLU
N+S/mb1l0MsA91PPRlZqovEFs6214zo8e31pXcWbLbfhbVY32pqDk4eG1JAFjjGM
oBASk7z1TH8Ealfj3xr8/HCbfyoaMcSmQzYdEr4E7XOp955IjRU91RrHxYjcxQ9f
0VUwak08k6hNBD502jstv5ePJBWoFXYXKuBlaH224Xvhe+9GnaUXsU8rI+OMHJes
g4/98+bparaQ8pjhLeh5BoIhojg0y4qNaFmbJraZxG3uhyH0nDVfepOsUj4QOadS
VbuULwAy97q+UFwUWuVn/XVzrb3B35TpIJWpvJLF6w5f3pyy58XsVOfVmNyyGuyU
jMWUGxIGAsXZusCmsNs=
=qVV5
-----END PGP SIGNATURE-----

View file

@ -1,16 +0,0 @@
{
"last_digest": "564623a3afcedc19737f7002885bb62529bd8125f4dd6b0d6efd57ab768fb773",
"commands": [
{
"url": "/v1/auth/tee/tees/test",
"data": {
"lease": "1000",
"name": "test",
"sgx_allowed_tcb_levels": "Ok,SwHardeningNeeded",
"sgx_mrsigner": "c5591a72b8b86e0d8814d6e8750e3efe66aea2d102b8ba2405365559b858697d",
"token_policies": "test",
"types": "sgx"
}
}
]
}

View file

@ -1,14 +0,0 @@
-----BEGIN PGP SIGNATURE-----
iQHFBAABCAAvFiEEgaMSxZ1nnZMPqeiwbXKPKaLbq/gFAmTSQ7sRHHRlc3RAZXhh
bXBsZS5jb20ACgkQbXKPKaLbq/jd0Av/Zruw1+nsVFwVIF98IhK4rWoW4vV3Xims
wMpAxoV2wTlk55xHbGkWa0BHnjvJr3XlwyJtEI4fT4rL53AgsSd/kp0/2Ml6ExgN
LGgziTf+Ct6oHPSM8YDtBXWtFv8YwieSxTYNA1hlCkwpN1d1k8JLZj4i1+U1AhPL
nYtM2PLOxxkdFCx7ecp2GCKqsZa1qR+dc+igAHJ4mKKKR9bhhtTxf1tH3nf1BAWu
G3Kw+CZISX4IvuUFVC/AA/+g2ySBoLhmk01lzjYvlJHQyJiOlSpHo1nGBvQHZ9cO
32I0dn+GOlnzCnsYX4JBsiGrNXG1PfEqL0MbNdrVf8grRC96zwQ9ITio25DaRe1r
rO+i68aAS3f9XGGJ1YHUKCAAmCnwPz+x34gH6r6r8LOI+ZRnFKbUDd7Fbuy47EXb
oKe3/oGfnY6avn+/2dR1qeHwnN+CSW64UDChWFXyxCD17go7crGq1MhpNvlmmuxW
HRI4+gMa4DpiCrzDgCS4ZVaZyzIwIvWw
=X/H8
-----END PGP SIGNATURE-----