Skip to content

Commit

Permalink
Genesis data (#7)
Browse files Browse the repository at this point in the history
Co-authored-by: ssolit <sol@seismic.systems>
  • Loading branch information
ssolit and ssolit authored Oct 23, 2024
1 parent d1a1310 commit ae1785a
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 15 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ env_logger = "0.11"
once_cell = "1.20.2"
kbs-types = "0.7.0"
serial_test = "3.1.0"
bincode = "1.3.3"
79 changes: 67 additions & 12 deletions src/coco_aa/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
use attestation_agent::{AttestationAPIs, AttestationAgent};
use attestation_agent::AttestationAPIs;
use hyper::{body::to_bytes, Body, Request, Response};
use once_cell::sync::Lazy;
use std::convert::Infallible;
use std::sync::Arc;

use super::structs::*;
use crate::utils::respone_utils::{invalid_json_body_resp, invalid_req_body_resp};

// Initialize an Arc-wrapped AttestationAgent lazily
// the attestation agent provides APIs to interact with the secure hardware features
static ATTESTATION_AGENT: Lazy<Arc<AttestationAgent>> = Lazy::new(|| {
let config_path = None;
Arc::new(AttestationAgent::new(config_path).expect("Failed to create an AttestationAgent"))
});
use crate::ATTESTATION_AGENT;

/// Handles attestation evidence request.
///
Expand Down Expand Up @@ -49,7 +41,8 @@ pub async fn attestation_get_evidence_handler(
};

// Get the evidence from the attestation agent
let evidence = ATTESTATION_AGENT
let coco_aa = ATTESTATION_AGENT.get().unwrap();
let evidence = coco_aa
.get_evidence(evidence_request.runtime_data.as_slice())
.await
.map_err(|e| format!("Error while getting evidence: {:?}", e))
Expand All @@ -64,14 +57,20 @@ pub async fn attestation_get_evidence_handler(
#[cfg(test)]
mod tests {
use super::*;
use crate::init_coco_aa;
use crate::utils::test_utils::is_sudo;
use hyper::{Body, Request, Response, StatusCode};
use serde_json::Value;
use serial_test::serial;

#[tokio::test]
async fn test_attestation_evidence_handler_valid_request() {
#[serial(attestation_agent)]
async fn test_attestation_evidence_handler_valid_request_sample() {
// NOTE: This test will run with the Sample TEE Type
// because it doesn't run with sudo privileges

init_coco_aa().expect("Failed to initialize AttestationAgent");

// Mock a valid AttestationGetEvidenceRequest
let runtime_data = "nonce".as_bytes(); // Example runtime data
let evidence_request = AttestationGetEvidenceRequest {
Expand Down Expand Up @@ -104,6 +103,62 @@ mod tests {
assert!(!get_evidence_resp.evidence.is_empty());
}

#[tokio::test]
#[serial(attestation_agent)]
async fn test_attestation_evidence_handler_aztdxvtpm_runtime_data() {
// handle set up permissions
if !is_sudo() {
eprintln!("test_eval_evidence_az_tdx: skipped (requires sudo privileges)");
return;
}

init_coco_aa().expect("Failed to initialize AttestationAgent");

// Make requests with different runtime data and see they are different
let runtime_data_1 = "nonce1".as_bytes();
let evidence_request_1 = AttestationGetEvidenceRequest {
runtime_data: runtime_data_1.to_vec(),
};

let runtime_data_2 = "nonce2".as_bytes();
let evidence_request_2 = AttestationGetEvidenceRequest {
runtime_data: runtime_data_2.to_vec(),
};

// Serialize the request to JSON
let payload_json_1 = serde_json::to_string(&evidence_request_1).unwrap();
let payload_json_2 = serde_json::to_string(&evidence_request_2).unwrap();

// Create a request
let req_1 = Request::builder()
.method("POST")
.uri("/attestation/attester/evidence")
.header("Content-Type", "application/json")
.body(Body::from(payload_json_1))
.unwrap();
let res_1: Response<Body> = attestation_get_evidence_handler(req_1).await.unwrap();
let req_2 = Request::builder()
.method("POST")
.uri("/attestation/attester/evidence")
.header("Content-Type", "application/json")
.body(Body::from(payload_json_2))
.unwrap();
let res_2: Response<Body> = attestation_get_evidence_handler(req_2).await.unwrap();

assert_eq!(res_1.status(), StatusCode::OK);
assert_eq!(res_2.status(), StatusCode::OK);

// Parse and check the response body
let body_1 = hyper::body::to_bytes(res_1.into_body()).await.unwrap();
let body_2 = hyper::body::to_bytes(res_2.into_body()).await.unwrap();
let get_evidence_resp_1: AttestationGetEvidenceResponse =
serde_json::from_slice(&body_1).unwrap();
let get_evidence_resp_2: AttestationGetEvidenceResponse =
serde_json::from_slice(&body_2).unwrap();

assert_ne!(get_evidence_resp_1.evidence, get_evidence_resp_2.evidence);
}

#[tokio::test]
async fn test_attestation_evidence_handler_invalid_json() {
// Create a request with invalid JSON body
Expand Down
1 change: 1 addition & 0 deletions src/coco_aa/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct AttestationGetEvidenceRequest {
// For AzTdxVtpm, this affects the quotes's aztdxvtpm.quote.body.report_data
pub runtime_data: Vec<u8>,
}

Expand Down
3 changes: 2 additions & 1 deletion src/coco_as/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ use crate::ATTESTATION_SERVICE;
/// The attestation service checks the following criteria:
///
/// 1. **Internal Consistency of Evidence:**
/// - Verifies that the provided evidence is consistent with itself.
/// - Verifies that the provided evidence is consistent with itself through the AS verifier dependency
/// - Ex checks that the evidence data matches the TEE's signature.
/// - Ex checks that the init_data and runtime_data in the request match with the attestation evidence.
/// - Also includes a call to the PCCS to verify the TEE public key is valid
///
/// 2. **Comparison with Reference Values (RVPS):**
/// - Validates the evidence against trusted reference values provided by the **Reference Value Provider Service (RVPS)**.
Expand Down
145 changes: 145 additions & 0 deletions src/genesis/handlers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use attestation_agent::AttestationAPIs;
use hyper::{Body, Request, Response};
use sha2::{Digest, Sha256};
use std::convert::Infallible;

use super::structs::*;
use crate::utils::crypto_utils::get_sample_secp256k1_pk;
use crate::ATTESTATION_AGENT;

/// Handles request to get genesis data.
///
/// At genesis the network generates network wide constants, such as the transaction encryption keypair
/// This function returns the genesis data to the client
/// Along with an attestation of such data that can be verified with the attestation/as/eval_evidence endpoint
///
/// Currently uses hardcoded values for testing purposes, which will be updated later
pub async fn genesis_get_data_handler(_: Request<Body>) -> Result<Response<Body>, Infallible> {
// For now, we load the keypair from a file
let io_pk = get_sample_secp256k1_pk();

// For now the genesis data is just the public key of the IO encryption keypair
// But this is expected to change in the future
let genesis_data = GenesisData { io_pk };

// hash the genesis data and attest to it
let genesis_data_bytes = genesis_data.to_bytes();
let hash_bytes: [u8; 32] = Sha256::digest(genesis_data_bytes).into();

// Get the evidence from the attestation agent
let aa_clone = ATTESTATION_AGENT.clone();
let coco_aa = aa_clone.get().unwrap();
let evidence = coco_aa
.get_evidence(&hash_bytes)
.await
.map_err(|e| format!("Error while getting evidence: {:?}", e))
.unwrap();

// Return the evidence as a response
let response_body = GenesisDataResponse {
data: genesis_data,
evidence,
};
let response_json = serde_json::to_string(&response_body).unwrap();
Ok(Response::new(Body::from(response_json)))
}

#[allow(unused_imports)]
#[cfg(test)]
mod tests {
use super::*;
use crate::attestation_eval_evidence_handler;
use crate::coco_as::structs::AttestationEvalEvidenceRequest;
use crate::coco_as::structs::AttestationEvalEvidenceResponse;
use crate::utils::test_utils::is_sudo;
use crate::{init_coco_aa, init_coco_as};
use attestation_service::Data;
use hyper::{Body, Request, Response, StatusCode};
use kbs_types::Tee;
use serde_json::Value;
use serial_test::serial;

#[tokio::test]
#[serial(attestation_agent)]
async fn test_genesis_get_data_handler_success_basic() {
// Initialize ATTESTATION_AGENT
init_coco_aa().expect("Failed to initialize AttestationAgent");

// Create a request
let req = Request::builder()
.method("GET")
.uri("/genesis/data")
.body(Body::empty())
.unwrap();

// Call the handler
let res: Response<Body> = genesis_get_data_handler(req).await.unwrap();

// Check that the response status is 200 OK
assert_eq!(res.status(), StatusCode::OK);

// Parse and check the response body
let body_bytes = hyper::body::to_bytes(res.into_body()).await.unwrap();
let response: GenesisDataResponse = serde_json::from_slice(&body_bytes).unwrap();

// assert that the attestation is not empty
assert!(!response.evidence.is_empty());
}

#[tokio::test]
#[serial(attestation_agent, attestation_service)]
async fn test_genesis_get_data_handler_evidence_verifies() {
// handle set up permissions
if !is_sudo() {
eprintln!("test_eval_evidence_az_tdx: skipped (requires sudo privileges)");
return;
}

// Initialize ATTESTATION_AGENT
init_coco_aa().expect("Failed to initialize AttestationAgent");

// Make a genesis data request
let req = Request::builder()
.method("GET")
.uri("/genesis/data")
.body(Body::empty())
.unwrap();
let res: Response<Body> = genesis_get_data_handler(req).await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let body_bytes = hyper::body::to_bytes(res.into_body()).await.unwrap();
let genesis_data_response: GenesisDataResponse =
serde_json::from_slice(&body_bytes).unwrap();

// Submit the genesis data to the attestation service
init_coco_as()
.await
.expect("Failed to initialize AttestationService");

let genesis_data_hash: [u8; 32] =
Sha256::digest(genesis_data_response.data.to_bytes()).into();

let tdx_eval_request = AttestationEvalEvidenceRequest {
evidence: genesis_data_response.evidence,
tee: Tee::AzTdxVtpm,
runtime_data: Some(Data::Raw(genesis_data_hash.to_vec())), // Check that the genesis data hash matches the evidence report_data
runtime_data_hash_algorithm: None,
};
let payload_json = serde_json::to_string(&tdx_eval_request).unwrap();
let req = Request::builder()
.method("POST")
.uri("/attestation/as/eval_evidence")
.header("Content-Type", "application/json")
.body(Body::from(payload_json))
.unwrap();
let res: Response<Body> = attestation_eval_evidence_handler(req).await.unwrap();

// Check that the eval evidence response
assert_eq!(res.status(), StatusCode::OK);
// Parse and check the response body
let body = hyper::body::to_bytes(res.into_body()).await.unwrap();
let eval_evidence_response: AttestationEvalEvidenceResponse =
serde_json::from_slice(&body).unwrap();

assert!(eval_evidence_response.eval);
}
}
2 changes: 2 additions & 0 deletions src/genesis/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod handlers;
pub mod structs;
26 changes: 26 additions & 0 deletions src/genesis/structs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use bincode;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct GenesisData {
pub io_pk: secp256k1::PublicKey,
}

#[allow(dead_code)]
impl GenesisData {
// Serialize the struct to bytes
pub fn to_bytes(&self) -> Vec<u8> {
bincode::serialize(self).expect("Failed to serialize")
}

// Deserialize the struct from bytes
pub fn from_bytes(bytes: &[u8]) -> Self {
bincode::deserialize(bytes).expect("Failed to deserialize")
}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct GenesisDataResponse {
pub data: GenesisData,
pub evidence: Vec<u8>,
}
28 changes: 26 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod coco_aa;
mod coco_as;
mod genesis;
mod signing;
mod tx_io;
mod utils;
Expand All @@ -14,24 +15,30 @@ use std::sync::Arc;

use coco_aa::handlers::*;
use coco_as::handlers::*;
use genesis::handlers::*;
use signing::handlers::*;
use tx_io::handlers::*;

use attestation_agent::AttestationAgent;
use attestation_service::{config::Config, AttestationService};

static ATTESTATION_SERVICE: OnceCell<Arc<AttestationService>> = OnceCell::new();
static ATTESTATION_AGENT: OnceCell<Arc<AttestationAgent>> = OnceCell::new();

#[tokio::main]
async fn main() -> Result<()> {
// Initialize the logger
env_logger::init();

// Initialize the AttestationService
// Initialize the Attestation Agent and Attestation Service
init_coco_aa()?;
init_coco_as().await?;

// create the server
let addr = SocketAddr::from(([127, 0, 0, 1], 7878));
let router = Router::builder()
.middleware(Middleware::pre(logger))
.get("/genesis/data", genesis_get_data_handler)
.post(
"/attestation/aa/get_evidence",
attestation_get_evidence_handler,
Expand All @@ -41,7 +48,7 @@ async fn main() -> Result<()> {
attestation_eval_evidence_handler,
)
.post("/signing/sign", secp256k1_sign_handler)
.post("/siging/verify", secp256k1_verify_handler)
.post("/signing/verify", secp256k1_verify_handler)
.post("/tx_io/encrypt", tx_io_encrypt_handler)
.post("/tx_io/decrypt", tx_io_decrypt_handler)
.err_handler_with_info(error_handler)
Expand Down Expand Up @@ -81,6 +88,23 @@ async fn error_handler(err: routerify::RouteError, _: RequestInfo) -> Response<B
.unwrap()
}

fn init_coco_aa() -> Result<()> {
// Check if the service is already initialized
// This helps with multithreaded testing
if ATTESTATION_AGENT.get().is_some() {
// AttestationAgent is already initialized, so we skip re-initialization.
return Ok(());
}

let config_path = None;
let coco_aa = AttestationAgent::new(config_path).expect("Failed to create an AttestationAgent");
ATTESTATION_AGENT
.set(Arc::new(coco_aa))
.map_err(|_| anyhow::anyhow!("Failed to set AttestationAgent"))?;

Ok(())
}

async fn init_coco_as() -> Result<()> {
// Check if the service is already initialized
// This helps with multithreaded testing
Expand Down

0 comments on commit ae1785a

Please sign in to comment.