Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api-server-rest: Add extend_runtime_measurement API #635

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions api-server-rest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,11 @@ $ curl http://127.0.0.1:8006/aa/evidence\?runtime_data\=xxxx

$ curl http://127.0.0.1:8006/aa/token\?token_type\=kbs
{"token":"eyJhbGciOiJFi...","tee_keypair":"-----BEGIN... "}

$ curl http://127.0.0.1:8006/aa/extend_runtime_measurement\?domain\=image-rs\&operation\=pull_image\&content\=docker.io\/library\/busybox@sha256:50aa4698fa6262977cff89181b2664b99d8a56dbca847bf62f2ef04854597cf8\&register_index=17
runtime measurement extend success

$ cat /run/attestation-agent/eventlog
INIT sha384/000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
image-rs pull_image docker.io/library/busybox@sha256:50aa4698fa6262977cff89181b2664b99d8a56dbca847bf62f2ef04854597cf8
```
23 changes: 22 additions & 1 deletion api-server-rest/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,27 @@ fn _token() {}
)]
fn _evidence() {}

#[utoipa::path(
get,
path = "/aa/extend_runtime_measurement",
params(
("domain" = String, Query, description = "Domain of event entry belongs"),
("operation" = String, Query, description = "Operation type of event entry records"),
("content" = String, Query, description = "Detaile content of the operation"),
("register_index" = String, Query, description = "PCR registry to be extended with (optional)")
),
responses(
(status = 200, description = "runtime measurement extend success",
content_type = "application/octet-stream",
body = String,
example = json!({"domain":"image-rs","operation":"CreateContainer", "content":"docker.io/library/alpine", "register_index":"17"})),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's update the example when the spec is finished.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we'll update this part to follow the spec.

(status = 400, description = "bad request for invalid query param"),
(status = 403, description = "forbid external access"),
(status = 405, description = "only Get method allowed")
)
)]
fn _extend_runtime_measurement() {}

#[utoipa::path(
get,
path = "/cdh/resource/{repository}/{type}/{tag}",
Expand All @@ -72,7 +93,7 @@ fn generate_openapi_document() -> std::io::Result<()> {
(url = "http://127.0.0.1:8006", description = "CoCo Restful API")
),

paths(_token, _evidence, _resource)
paths(_token, _evidence, _extend_runtime_measurement, _resource)
)]
struct ApiDoc;
let mut file = File::create("openapi/api.json")?;
Expand Down
73 changes: 73 additions & 0 deletions api-server-rest/openapi/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,79 @@
}
}
},
"/aa/extend_runtime_measurement": {
"get": {
"tags": [
"crate"
],
"operationId": "_extend_runtime_measurement",
"parameters": [
{
"name": "domain",
"in": "query",
"description": "Domain of event entry belongs",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "operation",
"in": "query",
"description": "Operation type of event entry records",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "content",
"in": "query",
"description": "Detaile content of the operation",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "register_index",
"in": "query",
"description": "PCR registry to be extended with (optional)",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "runtime measurement extend success",
"content": {
"application/octet-stream": {
"schema": {
"type": "string"
},
"example": {
"content": "docker.io/library/alpine",
"domain": "image-rs",
"operation": "CreateContainer",
"register_index": "17"
}
}
}
},
"400": {
"description": "bad request for invalid query param"
},
"403": {
"description": "forbid external access"
},
"405": {
"description": "only Get method allowed"
}
}
}
},
"/aa/token": {
"get": {
"tags": [
Expand Down
25 changes: 25 additions & 0 deletions api-server-rest/protos/attestation_agent.proto
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,32 @@ message GetTokenResponse {
bytes Token = 1;
}

// - `events`: a event slice. Any single event will be calculated into a hash digest to extend the current
// platform's RTMR.
// - `register_index`: a target PCR that will be used to extend RTMR. Note that different platform
// would have its own strategy to map a PCR index into a architectual RTMR index. If not given, a default one
// will be used.
// Extend RuntimeMeasurement by
// - add a record in eventlog
// - extend RTMR due to platform
message ExtendRuntimeMeasurementRequest {
// The domain to which this event entry belongs. This domain is used to distinguish the semantics of log entries in different contexts.
string Domain = 1;

// Concrete operation type that this event entry records.
string Operation = 2;

// Detailed content of the operation that this event entry records.
string Content = 3;

// Which PCR will be extended with the hash of this entry.
optional uint64 RegisterIndex = 4;
}

message ExtendRuntimeMeasurementResponse {}

service AttestationAgentService {
rpc GetEvidence(GetEvidenceRequest) returns (GetEvidenceResponse) {};
rpc GetToken(GetTokenRequest) returns (GetTokenResponse) {};
rpc ExtendRuntimeMeasurement(ExtendRuntimeMeasurementRequest) returns (ExtendRuntimeMeasurementResponse) {};
}
63 changes: 56 additions & 7 deletions api-server-rest/src/aa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
//

use crate::router::ApiHandler;
use crate::ttrpc_proto::attestation_agent::{GetEvidenceRequest, GetTokenRequest};
use crate::ttrpc_proto::attestation_agent::{
ExtendRuntimeMeasurementRequest, GetEvidenceRequest, GetTokenRequest,
};
use crate::ttrpc_proto::attestation_agent_ttrpc::AttestationAgentServiceClient;
use anyhow::*;
use async_trait::async_trait;
Expand All @@ -20,6 +22,7 @@ pub const AA_ROOT: &str = "/aa";
/// URL for querying CDH get resource API
const AA_TOKEN_URL: &str = "/token";
const AA_EVIDENCE_URL: &str = "/evidence";
const AA_MEASUREMENT_URL: &str = "/extend_runtime_measurement";

pub struct AAClient {
client: AttestationAgentServiceClient,
Expand Down Expand Up @@ -50,17 +53,13 @@ impl ApiHandler for AAClient {
.map(|v| form_urlencoded::parse(v.as_bytes()).into_owned().collect())
.unwrap_or_default();

if params.len() != 1 {
return self.not_allowed();
}

match url_path {
AA_TOKEN_URL => match params.get("token_type") {
Some(token_type) => match self.get_token(token_type).await {
std::result::Result::Ok(results) => return self.octet_stream_response(results),
Err(e) => return self.internal_error(e.to_string()),
},
None => return self.bad_request(),
None => return self.internal_error("invalid param: token_type None!".to_string()),
},
AA_EVIDENCE_URL => match params.get("runtime_data") {
Some(runtime_data) => {
Expand All @@ -71,8 +70,38 @@ impl ApiHandler for AAClient {
Err(e) => return self.internal_error(e.to_string()),
}
}
None => return self.bad_request(),
None => {
return self.internal_error("invalid param: runtime_data None!".to_string())
}
},
AA_MEASUREMENT_URL => {
let domain = params.get("domain");
let operation = params.get("operation");
let content = params.get("content");
match (domain, operation, content) {
(Some(domain), Some(operation), Some(content)) => {
let register_index: Option<u64> = params
.get("register_index")
.and_then(|value| value.parse::<u64>().ok());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a case where register_index is given by caller but failed to be parsed. The register_index will be None thus inside AA a default one. The caller should know about this, or return an error when parsing fails. wdyt?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, return error is make sense for me, I'll update this part.


match self
.extend_runtime_measurement(domain, operation, content, register_index)
.await
{
std::result::Result::Ok(results) => {
return self.octet_stream_response(results)
}
Err(e) => return self.internal_error(e.to_string()),
}
}
_ => {
return self.internal_error(format!(
"invalid params: domain {:?}, operation {:?}, content {:?}!",
domain, operation, content
))
}
}
}

_ => {
return self.not_found();
Expand Down Expand Up @@ -116,4 +145,24 @@ impl AAClient {
.await?;
Ok(res.Evidence)
}

pub async fn extend_runtime_measurement(
&self,
domain: &str,
operation: &str,
content: &str,
register_index: Option<u64>,
) -> Result<Vec<u8>> {
let req = ExtendRuntimeMeasurementRequest {
Domain: domain.to_string(),
Operation: operation.to_string(),
Content: content.to_string(),
RegisterIndex: register_index,
..Default::default()
};
self.client
.extend_runtime_measurement(ttrpc::context::with_timeout(TTRPC_TIMEOUT), &req)
.await?;
Ok("runtime measurement extend success".into())
}
}
Loading
Loading