Skip to content

Commit

Permalink
Rust sdk eventlog ima support (#67)
Browse files Browse the repository at this point in the history
* docs(contributor): contrib-readme-action has updated readme

* this commit includes:
- enable IMA event log retrieving and parsing
- add intermediate format and function for eventlog handling both TCG
  fornat and canonical format

* update accoridng to review comments

* update code format

* bug fix for ima log parsing

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
hairongchen and github-actions[bot] authored Jan 24, 2024
1 parent 9081317 commit a6d946e
Show file tree
Hide file tree
Showing 10 changed files with 280 additions and 60 deletions.
4 changes: 4 additions & 0 deletions common/rust/cctrusted_base/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
name = "cctrusted_base"
version = "0.1.0"
edition = "2021"
authors = ["Chen Hairong <hairong.chen@intel.com>", "Lu Ken <ken.lu@intel.com>"]
repository = "https://github.com/cc-api/cc-trusted-api"
description = "CC Trusted API Base SDK"
license = "Apache-2.0"

[lib]
Expand All @@ -15,3 +18,4 @@ log = "0.4.20"
sha2 = "0.10"
lazy_static = "1.4.0"
hashbrown = "0.14"
hex = "0.4.3"
2 changes: 1 addition & 1 deletion common/rust/cctrusted_base/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub trait CCTrustedApi {
OS type and cloud native type event beyond the measured-boot.
Returns:
EventLogEntry struct
Vector of EventLogEntry
*/
fn get_cc_eventlog(
start: Option<u32>,
Expand Down
238 changes: 203 additions & 35 deletions common/rust/cctrusted_base/src/eventlog.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,102 @@
use crate::binary_blob::*;
use crate::tcg::EventLogEntry;
use crate::tcg::TcgDigest;
use crate::tcg::TcgEfiSpecIdEvent;
use crate::tcg::TcgEfiSpecIdEventAlgorithmSize;
use crate::tcg::TcgImrEvent;
use crate::tcg::TcgPcClientImrEvent;
use crate::tcg::EV_NO_ACTION;
use crate::tcg::*;
use anyhow::anyhow;
use hashbrown::HashMap;
use hex;

/***
TcgEventLog struct.
This class contains the event logs following TCG specification.
* This is the common struct for tcg event logs to be delivered in different formats.
Currently TCG supports several event log formats defined in TCG_PCClient Spec,
Canonical Eventlog Spec, etc.
This struct provides the functionality to convey event logs in different format
according to request.
Attributes:
rec_num: contains the record number of the event log within the imr index
imr_index: the index of the register that the event log belongs to
event_type: event type of the event log
digests: a list of TcgDigest objects
event_size: size of the event
event: raw event information
extra_info: extra information in the event
*/
#[derive(Clone)]
pub struct TcgEventLog {
pub rec_num: u32,
pub imr_index: u32,
pub event_type: u32,
pub digests: Vec<TcgDigest>,
pub event_size: u32,
pub event: Vec<u8>,
pub extra_info: HashMap<String, String>,
}

impl TcgEventLog {
fn format_event_log(&self, parse_format: u8) -> EventLogEntry {
match parse_format {
TCG_PCCLIENT_FORMAT => self.to_tcg_pcclient_format(),
TCG_CANONICAL_FORMAT => self.to_tcg_canonical_format(),
0_u8 | 3_u8..=u8::MAX => todo!(),
}
}

fn to_tcg_pcclient_format(&self) -> EventLogEntry {
if self.event_type == EV_NO_ACTION {
return EventLogEntry::TcgPcClientImrEvent(TcgPcClientImrEvent {
imr_index: self.imr_index,
event_type: self.event_type,
digest: self.digests[0].hash[0..20].try_into().unwrap(),
event_size: self.event_size,
event: self.event.clone(),
});
}

EventLogEntry::TcgImrEvent(TcgImrEvent {
imr_index: self.imr_index,
event_type: self.event_type,
digests: self.digests.clone(),
event_size: self.event_size,
event: self.event.clone(),
})
}

fn to_tcg_canonical_format(&self) -> EventLogEntry {
todo!()
}
}

/***
EventLogs struct.
This struct contains the all event logs available on the system.
Attributes:
data: raw data containing all boot time event logs
boot_time_data: raw data containing all boot time event logs
runtime_data: raw data containing runtime event logs(now IMA events)
event_logs: all parsed event logs
count: total number of event logs
parse_format: event log format used
*/
pub struct TcgEventLog {
#[derive(Clone)]
pub struct EventLogs {
pub spec_id_header_event: TcgEfiSpecIdEvent,
pub data: Vec<u8>,
pub boot_time_data: Vec<u8>,
pub run_time_data: Vec<String>,
pub event_logs: Vec<EventLogEntry>,
pub count: u32,
pub parse_format: u8,
pub event_logs_record_number_list: [u32; 24],
}

impl TcgEventLog {
pub fn new(data: Vec<u8>) -> TcgEventLog {
TcgEventLog {
impl EventLogs {
pub fn new(boot_time_data: Vec<u8>, run_time_data: Vec<String>, parse_format: u8) -> EventLogs {
EventLogs {
spec_id_header_event: TcgEfiSpecIdEvent::new(),
data,
boot_time_data,
run_time_data,
event_logs: Vec::new(),
count: 0,
parse_format,
event_logs_record_number_list: [0; 24],
}
}

Expand Down Expand Up @@ -74,33 +141,49 @@ impl TcgEventLog {
Ok((self.event_logs[begin as usize..end as usize]).to_vec())
}

/***
Fetch the record number maintained separately by index.
Increment the number to be prepared for next measurement.
Args:
imr_index: the imr index used to fetch certain record number
Returns:
The record number
*/
fn get_record_number(&mut self, imr_index: u32) -> u32 {
let rec_num = self.event_logs_record_number_list[imr_index as usize];
self.event_logs_record_number_list[imr_index as usize] += 1;
rec_num
}

/***
Parse event log data into TCG compatible forms.
Go through all event log data and parse the contents accordingly
Save the parsed event logs into TcgEventLog.
Save the parsed event logs into EventLogs.
*/
fn parse(&mut self) -> Result<bool, anyhow::Error> {
if self.data.is_empty() {
return Err(anyhow!("[parse] no eventlog data provided"));
if self.boot_time_data.is_empty() {
return Err(anyhow!("[parse] no boot time eventlog provided"));
}

let mut index = 0;
while index < self.data.len() {
while index < self.boot_time_data.len() {
let start = index;
let imr = get_u32(self.data[index..index + 4].to_vec());
let imr = get_u32(self.boot_time_data[index..index + 4].to_vec());
index += 4;
let event_type = get_u32(self.data[index..index + 4].to_vec());
let event_type = get_u32(self.boot_time_data[index..index + 4].to_vec());
if imr == 0xFFFFFFFF {
break;
}

if event_type == EV_NO_ACTION {
match self.parse_spec_id_event_log(self.data[start..].to_vec()) {
match self.parse_spec_id_event_log(self.boot_time_data[start..].to_vec()) {
Ok((spec_id_event, event_len)) => {
index = start + event_len as usize;
self.event_logs
.push(EventLogEntry::TcgPcClientImrEvent(spec_id_event));
self.count += 1
.push(spec_id_event.format_event_log(self.parse_format));
self.count += 1;
}
Err(e) => {
return Err(anyhow!(
Expand All @@ -110,11 +193,12 @@ impl TcgEventLog {
}
}
} else {
match self.parse_event_log(self.data[start..].to_vec()) {
match self.parse_event_log(self.boot_time_data[start..].to_vec()) {
Ok((event_log, event_len)) => {
index = start + event_len as usize;
self.event_logs.push(EventLogEntry::TcgImrEvent(event_log));
self.count += 1
self.event_logs
.push(event_log.format_event_log(self.parse_format));
self.count += 1;
}
Err(e) => {
return Err(anyhow!("[parse] error in parse_event_log function {:?}", e));
Expand All @@ -123,6 +207,24 @@ impl TcgEventLog {
}
}

if !self.run_time_data.is_empty() {
for index in 0..self.run_time_data.len() {
match self.parse_ima_event_log(&self.run_time_data[index].clone()) {
Ok(event_log) => {
self.event_logs
.push(event_log.format_event_log(self.parse_format));
self.count += 1;
}
Err(e) => {
return Err(anyhow!(
"[parse] error in parse_ima_event_log function {:?}",
e
));
}
};
}
}

Ok(true)
}

Expand All @@ -140,13 +242,13 @@ impl TcgEventLog {
Args:
data: event log data in bytes
Returns:
A TcgPcClientImrEvent containing the Specification ID version event
A common TcgEventLog containing the Specification ID version event
An int specifying the event size
*/
fn parse_spec_id_event_log(
&mut self,
data: Vec<u8>,
) -> Result<(TcgPcClientImrEvent, u32), anyhow::Error> {
) -> Result<(TcgEventLog, u32), anyhow::Error> {
let mut index = 0;

let imr_index = get_u32(data[index..index + 4].to_vec());
Expand All @@ -155,19 +257,30 @@ impl TcgEventLog {
let header_event_type = get_u32(data[index..index + 4].to_vec());
index += 4;

let digest = data[index..index + 20].try_into().unwrap();
let rec_num = self.get_record_number(header_imr);

let digest_hash = data[index..index + 20].try_into().unwrap();
index += 20;
let mut digests: Vec<TcgDigest> = Vec::new();
let digest = TcgDigest {
algo_id: TPM_ALG_ERROR,
hash: digest_hash,
};
digests.push(digest);

let header_event_size = get_u32(data[index..index + 4].to_vec());
index += 4;
let header_event = data[index..index + header_event_size as usize]
.try_into()
.unwrap();
let specification_id_header = TcgPcClientImrEvent {
let specification_id_header = TcgEventLog {
rec_num,
imr_index: header_imr,
event_type: header_event_type,
digest,
digests,
event_size: header_event_size,
event: header_event,
extra_info: HashMap::new(),
};

// Parse EFI Spec Id Event structure
Expand Down Expand Up @@ -240,7 +353,7 @@ impl TcgEventLog {
A TcgImrEvent containing the event information
An int specifying the event size
*/
fn parse_event_log(&self, data: Vec<u8>) -> Result<(TcgImrEvent, u32), anyhow::Error> {
fn parse_event_log(&mut self, data: Vec<u8>) -> Result<(TcgEventLog, u32), anyhow::Error> {
let mut index = 0;

let mut imr_index = get_u32(data[index..index + 4].to_vec());
Expand All @@ -249,6 +362,8 @@ impl TcgEventLog {
let event_type = get_u32(data[index..index + 4].to_vec());
index += 4;

let rec_num = self.get_record_number(imr_index);

// Fetch digest count and get each digest and its algorithm
let digest_count = get_u32(data[index..index + 4].to_vec());
index += 4;
Expand Down Expand Up @@ -291,14 +406,67 @@ impl TcgEventLog {
index += event_size as usize;

Ok((
TcgImrEvent {
TcgEventLog {
rec_num,
imr_index,
event_type,
digests,
event_size,
event,
extra_info: HashMap::new(),
},
index.try_into().unwrap(),
))
}

/***
Parse ascii IMA events gathered during runtime.
Sample event and format:
IMR index | Template hash | Template name | Event data according to template
10 1e762ca412a3ef388ddcab416e2eb382d9d1e356 ima-ng sha384:74ccc46104f42db070375e6876a23aeaa3c2ae458888475baaa171c3fb7001b0fc385ed08420d5f60620924fc64d0b80 /etc/lsb-release
Args:
event: IMA ascii raw event
Returns:
A TcgEventLog object containing the ima event log
*/
fn parse_ima_event_log(&mut self, data: &str) -> Result<TcgEventLog, anyhow::Error> {
/* after the split, the elements vec has following mapping:
elements[0] => IMR index
elements[1] => Template hash
elements[2] => Template name
elements[3] to end of vec => Event data according to template
*/
let elements: Vec<&str> = data.trim_matches(' ').split(' ').collect();

let imr_index: u32 = elements[0].parse().unwrap();
let rec_num = self.get_record_number(imr_index);

let event = elements[3..].join(" ").as_bytes().to_vec();
let event_size = event.len() as u32;

let mut digests: Vec<TcgDigest> = Vec::new();
let digest_size = elements[1].len() / 2;
let algo_id = TcgDigest::get_algorithm_id_from_digest_size(digest_size.try_into().unwrap());
let digest = TcgDigest {
algo_id,
hash: hex::decode(elements[1]).expect("Decoding failed"),
};
digests.push(digest);

let mut extra_info = HashMap::new();
extra_info.insert("template_name".to_string(), elements[2].to_string());

Ok(TcgEventLog {
rec_num,
imr_index,
event_type: IMA_MEASUREMENT_EVENT,
digests,
event_size,
event,
extra_info,
})
}
}
Loading

0 comments on commit a6d946e

Please sign in to comment.