Skip to content

Commit e2b24db

Browse files
committed
Adding /agent/info API to agent
Bumping API version to 2.2 Signed-off-by: Michael Peters <[email protected]>
1 parent 96d4bd4 commit e2b24db

File tree

5 files changed

+138
-14
lines changed

5 files changed

+138
-14
lines changed

keylime-agent/src/agent_handler.rs

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright 2023 Keylime Authors
3+
4+
use crate::common::JsonWrapper;
5+
use crate::{tpm, Error as KeylimeError, QuoteData};
6+
use actix_web::{web, HttpRequest, HttpResponse, Responder};
7+
use base64::{engine::general_purpose, Engine as _};
8+
use log::*;
9+
use serde::{Deserialize, Serialize};
10+
11+
#[derive(Serialize, Deserialize, Debug)]
12+
pub(crate) struct AgentInfo {
13+
pub agent_uuid: String,
14+
pub tpm_hash_alg: String,
15+
pub tpm_enc_alg: String,
16+
pub tpm_sign_alg: String,
17+
pub ak_handle: u32,
18+
}
19+
20+
// This is an Info request which gets some information about this keylime agent
21+
// It should return a AgentInfo object as JSON
22+
pub async fn info(
23+
req: HttpRequest,
24+
data: web::Data<QuoteData>,
25+
) -> impl Responder {
26+
debug!("Returning agent information");
27+
28+
let mut info = AgentInfo {
29+
agent_uuid: data.agent_uuid.clone(),
30+
tpm_hash_alg: data.hash_alg.to_string(),
31+
tpm_enc_alg: data.enc_alg.to_string(),
32+
tpm_sign_alg: data.sign_alg.to_string(),
33+
ak_handle: data.ak_handle.value(),
34+
};
35+
36+
let response = JsonWrapper::success(info);
37+
info!("GET info returning 200 response");
38+
HttpResponse::Ok().json(response)
39+
}
40+
41+
#[cfg(test)]
42+
#[cfg(feature = "testing")]
43+
mod tests {
44+
use super::*;
45+
use crate::common::API_VERSION;
46+
use actix_web::{test, web, App};
47+
48+
#[actix_rt::test]
49+
async fn test_agent_info() {
50+
let mut quotedata = QuoteData::fixture().unwrap(); //#[allow_ci]
51+
quotedata.hash_alg = keylime::algorithms::HashAlgorithm::Sha256;
52+
quotedata.enc_alg = keylime::algorithms::EncryptionAlgorithm::Rsa;
53+
quotedata.sign_alg = keylime::algorithms::SignAlgorithm::RsaSsa;
54+
quotedata.agent_uuid = "DEADBEEF".to_string();
55+
let data = web::Data::new(quotedata);
56+
let mut app =
57+
test::init_service(App::new().app_data(data.clone()).route(
58+
&format!("/{API_VERSION}/agent/info"),
59+
web::get().to(info),
60+
))
61+
.await;
62+
63+
let req = test::TestRequest::get()
64+
.uri(&format!("/{API_VERSION}/agent/info"))
65+
.to_request();
66+
67+
let resp = test::call_service(&app, req).await;
68+
assert!(resp.status().is_success());
69+
70+
let result: JsonWrapper<AgentInfo> = test::read_body_json(resp).await;
71+
assert_eq!(result.results.agent_uuid.as_str(), "DEADBEEF");
72+
assert_eq!(result.results.tpm_hash_alg.as_str(), "sha256");
73+
assert_eq!(result.results.tpm_enc_alg.as_str(), "rsa");
74+
assert_eq!(result.results.tpm_sign_alg.as_str(), "rsassa");
75+
}
76+
}

keylime-agent/src/common.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use tss_esapi::{
3636
/*
3737
* Constants and static variables
3838
*/
39-
pub const API_VERSION: &str = "v2.1";
39+
pub const API_VERSION: &str = "v2.2";
4040
pub const TPM_DATA_PCR: usize = 16;
4141
pub const IMA_PCR: usize = 10;
4242
pub static RSA_PUBLICKEY_EXPORTABLE: &str = "rsa placeholder";

keylime-agent/src/errors_handler.rs

+50-13
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ pub(crate) async fn app_default(req: HttpRequest) -> impl Responder {
2020
http::Method::GET => {
2121
error = 400;
2222
message = format!(
23-
"Not Implemented: Use /version or /{API_VERSION}/ interfaces"
23+
"Not Implemented: Use /version or /{API_VERSION} interfaces"
2424
);
2525
response = HttpResponse::BadRequest()
2626
.json(JsonWrapper::error(error, &message));
2727
}
2828
http::Method::POST => {
2929
error = 400;
3030
message =
31-
format!("Not Implemented: Use /{API_VERSION}/ interface");
31+
format!("Not Implemented: Use /{API_VERSION} interface");
3232
response = HttpResponse::BadRequest()
3333
.json(JsonWrapper::error(error, &message));
3434
}
@@ -62,14 +62,15 @@ pub(crate) async fn api_default(req: HttpRequest) -> impl Responder {
6262
match req.head().method {
6363
http::Method::GET => {
6464
error = 400;
65-
message = "Not Implemented: Use /keys/ or /quotes/ interfaces";
65+
message =
66+
"Not Implemented: Use /agent, /keys, or /quotes interfaces";
6667
response = HttpResponse::BadRequest()
6768
.json(JsonWrapper::error(error, message));
6869
}
6970
http::Method::POST => {
7071
error = 400;
7172
message =
72-
"Not Implemented: Use /keys/ or /notifications/ interfaces";
73+
"Not Implemented: Use /keys or /notifications interfaces";
7374
response = HttpResponse::BadRequest()
7475
.json(JsonWrapper::error(error, message));
7576
}
@@ -103,19 +104,19 @@ pub(crate) async fn keys_default(req: HttpRequest) -> impl Responder {
103104
match req.head().method {
104105
http::Method::GET => {
105106
error = 400;
106-
message = "URI not supported, only /pubkey and /verify are supported for GET in /keys/ interface";
107+
message = "URI not supported, only /pubkey and /verify are supported for GET in /keys interface";
107108
response = HttpResponse::BadRequest()
108109
.json(JsonWrapper::error(error, message));
109110
}
110111
http::Method::POST => {
111112
error = 400;
112-
message = "URI not supported, only /ukey and /vkey are supported for POST in /keys/ interface";
113+
message = "URI not supported, only /ukey and /vkey are supported for POST in /keys interface";
113114
response = HttpResponse::BadRequest()
114115
.json(JsonWrapper::error(error, message));
115116
}
116117
_ => {
117118
error = 405;
118-
message = "Method is not supported in /keys/ interface";
119+
message = "Method is not supported in /keys interface";
119120
response = HttpResponse::MethodNotAllowed()
120121
.insert_header(http::header::Allow(vec![
121122
http::Method::GET,
@@ -166,6 +167,37 @@ pub(crate) async fn quotes_default(req: HttpRequest) -> impl Responder {
166167
response
167168
}
168169

170+
pub(crate) async fn agent_default(req: HttpRequest) -> impl Responder {
171+
let error;
172+
let response;
173+
let message;
174+
175+
match req.head().method {
176+
http::Method::GET => {
177+
error = 400;
178+
message = "URI not supported, only /info is supported for GET in /agent interface";
179+
response = HttpResponse::BadRequest()
180+
.json(JsonWrapper::error(error, message));
181+
}
182+
_ => {
183+
error = 405;
184+
message = "Method is not supported in /agent interface";
185+
response = HttpResponse::MethodNotAllowed()
186+
.insert_header(http::header::Allow(vec![http::Method::GET]))
187+
.json(JsonWrapper::error(error, message));
188+
}
189+
};
190+
191+
warn!(
192+
"{} returning {} response. {}",
193+
req.head().method,
194+
error,
195+
message
196+
);
197+
198+
response
199+
}
200+
169201
pub(crate) async fn notifications_default(
170202
req: HttpRequest,
171203
) -> impl Responder {
@@ -343,6 +375,11 @@ mod tests {
343375
.await
344376
}
345377

378+
#[actix_rt::test]
379+
async fn test_agent_default() {
380+
test_default(web::resource("/").to(agent_default), "GET").await
381+
}
382+
346383
#[derive(Serialize, Deserialize)]
347384
struct DummyQuery {
348385
param: String,
@@ -395,10 +432,10 @@ mod tests {
395432
.error_handler(path_parser_error),
396433
)
397434
.service(
398-
web::resource("/v2.1/ok").route(web::get().to(dummy)),
435+
web::resource("/v2.2/ok").route(web::get().to(dummy)),
399436
)
400437
.service(
401-
web::resource("/v2.1/ok/{number}/{string}")
438+
web::resource("/v2.2/ok/{number}/{string}")
402439
.route(web::get().to(dummy_with_path)),
403440
)
404441
.service(
@@ -410,7 +447,7 @@ mod tests {
410447

411448
// Sanity well formed request
412449
let req = test::TestRequest::get()
413-
.uri("/v2.1/ok?param=Test")
450+
.uri("/v2.2/ok?param=Test")
414451
.set_json(&DummyPayload { field: 42 })
415452
.to_request();
416453

@@ -432,7 +469,7 @@ mod tests {
432469

433470
// Test JSON parsing error
434471
let req = test::TestRequest::get()
435-
.uri("/v2.1/ok?param=Test")
472+
.uri("/v2.2/ok?param=Test")
436473
.insert_header(http::header::ContentType::json())
437474
.set_payload("Not JSON")
438475
.to_request();
@@ -445,7 +482,7 @@ mod tests {
445482

446483
// Test Query parsing error
447484
let req = test::TestRequest::get()
448-
.uri("/v2.1/ok?test=query")
485+
.uri("/v2.2/ok?test=query")
449486
.set_json(&DummyPayload { field: 42 })
450487
.to_request();
451488
let resp = test::call_service(&app, req).await;
@@ -457,7 +494,7 @@ mod tests {
457494

458495
// Test Path parsing error
459496
let req = test::TestRequest::get()
460-
.uri("/v2.1/ok/something/42?test=query")
497+
.uri("/v2.2/ok/something/42?test=query")
461498
.set_json(&DummyPayload { field: 42 })
462499
.to_request();
463500
let resp = test::call_service(&app, req).await;

keylime-agent/src/main.rs

+10
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
// missing_docs: there is many functions missing documentations for now
3232
#![allow(unused, missing_docs)]
3333

34+
mod agent_handler;
3435
mod common;
3536
mod config;
3637
mod error;
@@ -861,6 +862,15 @@ async fn main() -> Result<()> {
861862
)
862863
.service(
863864
web::scope(&format!("/{API_VERSION}"))
865+
.service(
866+
web::scope("/agent")
867+
.service(web::resource("/info").route(
868+
web::get().to(agent_handler::info),
869+
))
870+
.default_service(web::to(
871+
errors_handler::agent_default,
872+
)),
873+
)
864874
.service(
865875
web::scope("/keys")
866876
.service(web::resource("/pubkey").route(

packit-ci.fmf

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- how: shell
1717
script:
1818
- ln -s $(pwd) /var/tmp/rust-keylime_sources
19+
- dnf makecache
1920
- systemctl disable --now dnf-makecache.service || true
2021
- systemctl disable --now dnf-makecache.timer || true
2122

0 commit comments

Comments
 (0)