Skip to content

Commit

Permalink
aproxy: Complete attestation with KBS protocol
Browse files Browse the repository at this point in the history
With serialized TEE evidence and key, the proxy can now transfer these
to the attestation server for evaluation.

KBS will first transfer the evidence to the /attest endpoint for
evaluation. Upon successful attestation, the client can then retrieve
resources from the server.

Co-developed-by: Stefano Garzarella <[email protected]>
Signed-off-by: Tyler Fanelli <[email protected]>
  • Loading branch information
tylerfanelli committed Jan 16, 2025
1 parent 5f845d8 commit bb6b1b7
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 1 deletion.
20 changes: 20 additions & 0 deletions aproxy/src/attest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use std::{
/// Attest an SVSM client session.
pub fn attest(stream: &mut UnixStream, http: &mut backend::HttpClient) -> anyhow::Result<()> {
negotiation(stream, http)?;
attestation(stream, http)?;

Ok(())
}
Expand Down Expand Up @@ -44,6 +45,25 @@ fn negotiation(stream: &mut UnixStream, http: &mut backend::HttpClient) -> anyho
Ok(())
}

/// Attestation phase of SVSM attestation. SVSM will send an attestation request containing the TEE
/// evidence. Proxy will respond with an attestation response containing the status
/// (success/failure) and an optional secret upon successful attestation.
fn attestation(stream: &mut UnixStream, http: &backend::HttpClient) -> anyhow::Result<()> {
let request: AttestationRequest = {
let payload = proxy_read(stream)?;
serde_json::from_slice(&payload)
.context("unable to deserialize attestation request from JSON")?
};

// Attest the TEE evidence with the server.
let response: AttestationResponse = http.attestation(request)?;

// Write the response from the attestation server to SVSM.
proxy_write(stream, response)?;

Ok(())
}

/// Read bytes from the UNIX socket connected to SVSM. With each write, SVSM first writes an 8-byte
/// header indicating the length of the buffer. Once the length is read, the buffer can be read.
fn proxy_read(stream: &mut UnixStream) -> anyhow::Result<Vec<u8>> {
Expand Down
80 changes: 79 additions & 1 deletion aproxy/src/backend/kbs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

use super::*;
use anyhow::Context;
use kbs_types::{Challenge, Request, Tee};
use kbs_types::{Attestation, Challenge, Request, Response, Tee, TeePubKey};
use reqwest::StatusCode;
use serde_json::Value;

#[derive(Clone, Copy, Debug, Default)]
Expand Down Expand Up @@ -68,4 +69,81 @@ impl AttestationProtocol for KbsProtocol {

Ok(resp)
}

/// With the serialized TEE evidence and key, complete the attestation. Serialize the evidence
/// and send it to the /attest endpoint of the KBS server. Upon a successful attestation, fetch
/// a secret (identified as "svsm_secret"). If able to successfully fetch the secret, return a
/// successful AttestationResponse with the secret included.
fn attestation(
&self,
http: &HttpClient,
request: AttestationRequest,
) -> anyhow::Result<AttestationResponse> {
// Create a KBS attestation object from the TEE evidence and key.
let attestation = Attestation {
tee_pubkey: match request.key {
AttestationKey::EC {
crv,
x_b64url,
y_b64url,
} => TeePubKey::EC {
crv,
alg: "EC".to_string(),
x: x_b64url,
y: y_b64url,
},
},
tee_evidence: Value::String(request.evidence),
};

// Attest TEE evidence at KBS /attest endpoint.
let http_resp = http
.cli
.post(format!("{}/kbs/v0/attest", http.url))
.json(&attestation)
.send()
.context("unable to POST to KBS /attest endpoint")?;

// The JSON response from the /attest endpoint is basically ignored here. Instead, we check
// the HTTP status to indicate successful attestation.
//
// FIXME
if http_resp.status() != StatusCode::OK {
return Ok(AttestationResponse {
success: false,
secret: None,
pub_key: None,
});
}

// Successful attestation. Fetch the secret (which should be stored as "svsm_secret" within
// the KBS's RVPS.
let http_resp = http
.cli
.post(format!("{}/kbs/v0/svsm_secret", http.url))
.send()
.context("unable to POST to KBS /attest endpoint")?;

// Unsuccessful attempt at retrieving secret.
if http_resp.status() != StatusCode::OK {
return Ok(AttestationResponse {
success: false,
secret: None,
pub_key: None,
});
}

let text = http_resp
.text()
.context("unable to read KBS /resource response")?;

let resp: Response = serde_json::from_str(&text)
.context("unable to convert KBS /resource response to KBS Response object")?;

Ok(AttestationResponse {
success: true,
secret: Some(resp.ciphertext),
pub_key: Some(resp.encrypted_key),
})
}
}
13 changes: 13 additions & 0 deletions aproxy/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ impl HttpClient {
Protocol::Kbs(mut kbs) => kbs.negotiation(self, req),
}
}

pub fn attestation(&self, req: AttestationRequest) -> anyhow::Result<AttestationResponse> {
// Depending on the underlying protocol of the attestation server, attest TEE evidence
// accoridngly.
match self.protocol {
Protocol::Kbs(kbs) => kbs.attestation(self, req),
}
}
}

/// Attestation Protocol identifier.
Expand Down Expand Up @@ -65,4 +73,9 @@ pub trait AttestationProtocol {
client: &mut HttpClient,
req: NegotiationRequest,
) -> anyhow::Result<NegotiationResponse>;
fn attestation(
&self,
client: &HttpClient,
req: AttestationRequest,
) -> anyhow::Result<AttestationResponse>;
}

0 comments on commit bb6b1b7

Please sign in to comment.