Skip to content

Commit a01b6f8

Browse files
committed
Add capabilities_negotiation structures
This change aims to include those structures that will be required to communicate capabilities negotiation information for Keylime Push model Resolves: keylime#933 Signed-off-by: Sergio Arroutbi <[email protected]>
1 parent 3b6c0ff commit a01b6f8

File tree

3 files changed

+369
-0
lines changed

3 files changed

+369
-0
lines changed

keylime/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod ip_parser;
77
pub mod list_parser;
88
pub mod registrar_client;
99
pub mod serialization;
10+
pub mod structures;
1011
pub mod tpm;
1112
pub mod version;
1213

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
// Define the structure for the AttestationRequest:
4+
#[derive(Serialize, Deserialize, Debug)]
5+
pub struct AttestationRequest {
6+
#[serde(rename(serialize = "data", deserialize = "data"))]
7+
pub data: RequestData,
8+
}
9+
#[derive(Serialize, Deserialize, Debug)]
10+
pub struct RequestData {
11+
#[serde(rename(serialize = "type", deserialize = "type"))]
12+
pub type_: String,
13+
pub attributes: Attributes,
14+
}
15+
16+
#[derive(Serialize, Deserialize, Debug)]
17+
pub struct Attributes {
18+
pub evidence_supported: Vec<EvidenceSupported>,
19+
pub boot_time: String,
20+
}
21+
22+
#[derive(Serialize, Deserialize, Debug)]
23+
pub struct EvidenceSupported {
24+
pub evidence_class: String,
25+
pub evidence_type: String,
26+
#[serde(skip_serializing_if = "Option::is_none")]
27+
pub agent_capabilities: Option<AgentCapabilities>,
28+
#[serde(skip_serializing_if = "Option::is_none")]
29+
pub version: Option<String>,
30+
}
31+
32+
#[derive(Serialize, Deserialize, Debug)]
33+
pub struct AgentCapabilities {
34+
pub spec_version: String,
35+
pub hash_algorithms: Vec<String>,
36+
pub signing_schemes: Vec<String>,
37+
pub attestation_keys: Vec<AttestationKeys>,
38+
}
39+
40+
#[derive(Serialize, Deserialize, Debug)]
41+
pub struct AttestationKeys {
42+
pub key_class: String,
43+
pub key_identifier: String,
44+
pub key_algorithm: String,
45+
pub public_hash: String,
46+
}
47+
48+
// Define the structure for the AttestationResponse:
49+
#[derive(Serialize, Deserialize, Debug)]
50+
pub struct AttestationResponse {
51+
#[serde(rename(serialize = "data", deserialize = "data"))]
52+
pub data: ResponseData,
53+
}
54+
55+
#[derive(Serialize, Deserialize, Debug)]
56+
pub struct ResponseData {
57+
#[serde(rename(serialize = "type", deserialize = "type"))]
58+
pub type_: String,
59+
pub attributes: ResponseAttributes,
60+
}
61+
62+
#[derive(Serialize, Deserialize, Debug)]
63+
pub struct ResponseAttributes {
64+
pub evidence_requested: Vec<EvidenceRequested>,
65+
pub boot_time: String,
66+
}
67+
68+
#[derive(Serialize, Deserialize, Debug)]
69+
pub struct EvidenceRequested {
70+
pub evidence_class: String,
71+
pub evidence_type: String,
72+
#[serde(skip_serializing_if = "Option::is_none")]
73+
pub chosen_parameters: Option<ChosenParameters>,
74+
#[serde(skip_serializing_if = "Option::is_none")]
75+
pub version: Option<String>,
76+
}
77+
78+
#[derive(Serialize, Deserialize, Debug)]
79+
pub struct ChosenParameters {
80+
#[serde(skip_serializing_if = "Option::is_none")]
81+
pub nonce: Option<String>,
82+
#[serde(skip_serializing_if = "Option::is_none")]
83+
pub pcr_selection: Option<Vec<i32>>,
84+
#[serde(skip_serializing_if = "Option::is_none")]
85+
pub hash_algorithm: Option<String>,
86+
#[serde(skip_serializing_if = "Option::is_none")]
87+
pub signing_scheme: Option<String>,
88+
#[serde(skip_serializing_if = "Option::is_none")]
89+
pub attestation_key: Option<AttestationKey>,
90+
#[serde(skip_serializing_if = "Option::is_none")]
91+
pub starting_offset: Option<i32>,
92+
}
93+
94+
#[derive(Serialize, Deserialize, Debug)]
95+
pub struct AttestationKey {
96+
pub key_class: String,
97+
pub key_identifier: String,
98+
pub key_algorithm: String,
99+
pub public_hash: String,
100+
}
101+
102+
#[cfg(test)]
103+
mod tests {
104+
use super::*;
105+
106+
#[test]
107+
fn serialize_request() {
108+
// Create a new AttestationRequest object and serialize it to JSON
109+
let request = AttestationRequest {
110+
data: RequestData {
111+
type_: "attestation".to_string(),
112+
attributes: Attributes {
113+
evidence_supported: vec![
114+
EvidenceSupported {
115+
evidence_class: "certification".to_string(),
116+
evidence_type: "tpm_quote".to_string(),
117+
agent_capabilities: Some(AgentCapabilities {
118+
spec_version: "2.0".to_string(),
119+
hash_algorithms: vec!["sha3_512".to_string()],
120+
signing_schemes: vec!["rsassa".to_string()],
121+
attestation_keys: vec![
122+
AttestationKeys {
123+
key_class: "private_key".to_string(),
124+
key_identifier: "att_key_identifier".to_string(),
125+
key_algorithm: "rsa".to_string(),
126+
public_hash: "cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411".to_string(),
127+
},
128+
],
129+
}),
130+
version: Some("2.1".to_string()),
131+
},
132+
],
133+
boot_time: "2024-11-12T16:21:17Z".to_string(),
134+
},
135+
},
136+
};
137+
let json = serde_json::to_string(&request).unwrap(); //#[allow_ci]
138+
println!("{}", json);
139+
assert_eq!(
140+
json,
141+
r#"{"data":{"type":"attestation","attributes":{"evidence_supported":[{"evidence_class":"certification","evidence_type":"tpm_quote","agent_capabilities":{"spec_version":"2.0","hash_algorithms":["sha3_512"],"signing_schemes":["rsassa"],"attestation_keys":[{"key_class":"private_key","key_identifier":"att_key_identifier","key_algorithm":"rsa","public_hash":"cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411"}]},"version":"2.1"}],"boot_time":"2024-11-12T16:21:17Z"}}}"#
142+
);
143+
}
144+
145+
#[test]
146+
fn deserialize_request() {
147+
// Create a JSON string and deserialize it to an AttestationRequest object
148+
let json = r#"
149+
{
150+
"data": {
151+
"type":"attestation",
152+
"attributes": {
153+
"evidence_supported":[{"evidence_class":"certification",
154+
"evidence_type":"tpm_quote",
155+
"agent_capabilities":{"spec_version":"2.0",
156+
"hash_algorithms":["sha3_512"],
157+
"signing_schemes":["rsassa"],
158+
"attestation_keys":[{"key_class":"private_key","key_identifier":"att_key_identifier",
159+
"key_algorithm":"rsa",
160+
"public_hash":"cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411"}]}},
161+
{"evidence_class":"full_log",
162+
"evidence_type":"mb_log",
163+
"version":"2.1"},
164+
{"evidence_class": "partial_log",
165+
"evidence_type": "ima_entries"}],
166+
"boot_time":"2024-11-12T16:21:17Z"
167+
}
168+
}
169+
}"#;
170+
let request: AttestationRequest = serde_json::from_str(json).unwrap(); //#[allow_ci]
171+
assert_eq!(request.data.type_, "attestation");
172+
assert_eq!(
173+
request.data.attributes.evidence_supported[0].evidence_class,
174+
"certification"
175+
);
176+
assert_eq!(
177+
request.data.attributes.evidence_supported[0].evidence_type,
178+
"tpm_quote"
179+
);
180+
let agent_capabilities = request.data.attributes.evidence_supported
181+
[0]
182+
.agent_capabilities
183+
.as_ref()
184+
.unwrap(); //#[allow_ci]
185+
assert_eq!(agent_capabilities.spec_version, "2.0");
186+
assert_eq!(agent_capabilities.hash_algorithms[0], "sha3_512");
187+
assert_eq!(agent_capabilities.signing_schemes[0], "rsassa");
188+
assert_eq!(
189+
agent_capabilities.attestation_keys[0].key_class,
190+
"private_key"
191+
);
192+
assert_eq!(
193+
agent_capabilities.attestation_keys[0].key_identifier,
194+
"att_key_identifier"
195+
);
196+
assert_eq!(
197+
agent_capabilities.attestation_keys[0].key_algorithm,
198+
"rsa"
199+
);
200+
assert_eq!(
201+
agent_capabilities
202+
.attestation_keys[0]
203+
.public_hash,
204+
"cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411"
205+
);
206+
assert_eq!(
207+
request.data.attributes.evidence_supported[1].evidence_class,
208+
"full_log"
209+
);
210+
assert_eq!(
211+
request.data.attributes.evidence_supported[1].evidence_type,
212+
"mb_log"
213+
);
214+
assert_eq!(
215+
request.data.attributes.evidence_supported[1].version,
216+
Some("2.1".to_string())
217+
);
218+
assert_eq!(
219+
request.data.attributes.evidence_supported[2].evidence_class,
220+
"partial_log"
221+
);
222+
assert_eq!(
223+
request.data.attributes.evidence_supported[2].evidence_type,
224+
"ima_entries"
225+
);
226+
assert_eq!(request.data.attributes.boot_time, "2024-11-12T16:21:17Z");
227+
}
228+
229+
#[test]
230+
fn serialize_response() {
231+
// Create a new AttestationResponse object and serialize it to JSON
232+
let response = AttestationResponse {
233+
data: ResponseData {
234+
type_: "attestation".to_string(),
235+
attributes: ResponseAttributes {
236+
evidence_requested: vec![
237+
EvidenceRequested {
238+
evidence_class: "certification".to_string(),
239+
evidence_type: "tpm_quote".to_string(),
240+
chosen_parameters: Some(ChosenParameters {
241+
nonce: Some("nonce".to_string()),
242+
pcr_selection: Some(vec![0]),
243+
hash_algorithm: Some("sha384".to_string()),
244+
signing_scheme: Some("rsassa".to_string()),
245+
attestation_key: Some(AttestationKey {
246+
key_class: "private_key".to_string(),
247+
key_identifier: "att_key_identifier".to_string(),
248+
key_algorithm: "rsa".to_string(),
249+
public_hash: "cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411".to_string(),
250+
}),
251+
starting_offset: None,
252+
}),
253+
version: None,
254+
},
255+
],
256+
boot_time: "2024-11-12T16:21:17Z".to_string(),
257+
},
258+
},
259+
};
260+
let json = serde_json::to_string(&response).unwrap(); //#[allow_ci]
261+
println!("{}", json);
262+
assert_eq!(
263+
json,
264+
r#"{"data":{"type":"attestation","attributes":{"evidence_requested":[{"evidence_class":"certification","evidence_type":"tpm_quote","chosen_parameters":{"nonce":"nonce","pcr_selection":[0],"hash_algorithm":"sha384","signing_scheme":"rsassa","attestation_key":{"key_class":"private_key","key_identifier":"att_key_identifier","key_algorithm":"rsa","public_hash":"cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411"}}}],"boot_time":"2024-11-12T16:21:17Z"}}}"#
265+
);
266+
}
267+
268+
#[test]
269+
fn deserialize_response() {
270+
// Create a JSON string and deserialize it to an AttestationResponse object
271+
let json = r#"
272+
{
273+
"data": {
274+
"type":"attestation",
275+
"attributes": {
276+
"evidence_requested":[{"evidence_class":"certification",
277+
"evidence_type":"tpm_quote",
278+
"chosen_parameters":{"nonce":"nonce",
279+
"pcr_selection":[0],
280+
"hash_algorithm":"sha384",
281+
"signing_scheme":"rsassa",
282+
"attestation_key":{"key_class":"private_key",
283+
"key_identifier":"att_key_identifier",
284+
"key_algorithm":"rsa",
285+
"public_hash":"cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411"}}},
286+
{"evidence_class": "full_log",
287+
"evidence_type": "mb_log",
288+
"version": "2.1"},
289+
{"evidence_class": "partial_log",
290+
"evidence_type": "ima_entries",
291+
"chosen_parameters": {"starting_offset": 25}}],
292+
"boot_time":"2024-11-12T16:21:17Z"
293+
}
294+
}
295+
}"#;
296+
let response: AttestationResponse =
297+
serde_json::from_str(json).unwrap(); //#[allow_ci]
298+
assert_eq!(response.data.type_, "attestation");
299+
assert_eq!(
300+
response.data.attributes.evidence_requested[0].evidence_class,
301+
"certification"
302+
);
303+
assert_eq!(
304+
response.data.attributes.evidence_requested[0].evidence_type,
305+
"tpm_quote"
306+
);
307+
let some_chosen_parameters =
308+
response.data.attributes.evidence_requested[0]
309+
.chosen_parameters
310+
.as_ref();
311+
assert!(some_chosen_parameters.is_some());
312+
let chosen_parameters = some_chosen_parameters.unwrap(); //#[allow_ci]
313+
assert_eq!(chosen_parameters.nonce, Some("nonce".to_string()));
314+
// check pcr_selection
315+
assert_eq!(chosen_parameters.pcr_selection.as_ref().unwrap()[0], 0); //#[allow_ci]
316+
assert_eq!(
317+
chosen_parameters.hash_algorithm,
318+
Some("sha384".to_string())
319+
);
320+
assert_eq!(
321+
chosen_parameters.signing_scheme,
322+
Some("rsassa".to_string())
323+
);
324+
let attestation_key =
325+
chosen_parameters.attestation_key.as_ref().unwrap(); //#[allow_ci]
326+
assert_eq!(attestation_key.key_class, "private_key");
327+
assert_eq!(attestation_key.key_identifier, "att_key_identifier");
328+
assert_eq!(attestation_key.key_algorithm, "rsa");
329+
assert_eq!(attestation_key
330+
.public_hash,
331+
"cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411"
332+
);
333+
assert_eq!(
334+
response.data.attributes.evidence_requested[1].evidence_class,
335+
"full_log"
336+
);
337+
assert_eq!(
338+
response.data.attributes.evidence_requested[1].evidence_type,
339+
"mb_log"
340+
);
341+
assert_eq!(
342+
response.data.attributes.evidence_requested[1].version,
343+
Some("2.1".to_string())
344+
);
345+
assert_eq!(
346+
response.data.attributes.evidence_requested[2].evidence_class,
347+
"partial_log"
348+
);
349+
assert_eq!(
350+
response.data.attributes.evidence_requested[2].evidence_type,
351+
"ima_entries"
352+
);
353+
let some_chosen_parameters =
354+
response.data.attributes.evidence_requested[2]
355+
.chosen_parameters
356+
.as_ref();
357+
assert!(some_chosen_parameters.is_some());
358+
let chosen_parameters = some_chosen_parameters.unwrap(); //#[allow_ci]
359+
assert_eq!(chosen_parameters.starting_offset, Some(25));
360+
assert_eq!(
361+
response.data.attributes.boot_time,
362+
"2024-11-12T16:21:17Z"
363+
);
364+
}
365+
}

keylime/src/structures/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mod capabilities_negotiation;
2+
3+
pub use capabilities_negotiation::*;

0 commit comments

Comments
 (0)