use crate::error::*;
mod ioctl;
pub use ioctl::*;
mod types;
use crate::certs::{builtin::HRK, ca, csv, Verifiable};
use codicon::Decoder;
use log::*;
use std::fs::{self, File, OpenOptions};
use std::io::{self};
use std::path::Path;
pub use types::*;
fn topology_sysfs_get_dcu_id(sysfs_node_id: u32) -> io::Result<u32> {
let path = format!(
"/sys/devices/virtual/kfd/kfd/topology/nodes/{}/gpu_id",
sysfs_node_id
);
fs::read_to_string(&path)?
.trim()
.parse::<u32>()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Failed to parse DCU ID"))
}
fn num_subdirs(dirpath: &str, prefix: &str) -> usize {
fs::read_dir(dirpath)
.map(|entries| {
entries
.filter_map(Result::ok)
.filter(|entry| {
let name = entry.file_name();
let name_lossy = name.to_string_lossy();
!(name_lossy == "." || name_lossy == "..")
&& (prefix.is_empty() || name_lossy.starts_with(prefix))
})
.count()
})
.unwrap_or(0)
}
pub struct DcuDevice(File);
impl DcuDevice {
pub fn new() -> io::Result<DcuDevice> {
OpenOptions::new()
.read(true)
.write(true)
.open("/dev/mkfd")
.map(DcuDevice)
}
pub fn get_report(&mut self, userdata: [u8; 64]) -> Result<Vec<AttestationReport>, Error> {
let num_node = num_subdirs("/sys/devices/virtual/kfd/kfd/topology/nodes", "");
let mut reports: Vec<AttestationReport> = Vec::with_capacity(num_node);
for node in 0..num_node {
trace!("Processing node {} of {}", node, num_node);
if let Ok(dcu_id) = topology_sysfs_get_dcu_id(node as u32) {
trace!("Found DCU ID: {}", dcu_id);
if dcu_id == 0 {
continue;
}
let mut args = MkfdIoctlSecurityAttestationArgs::new();
args.set_attestation_args(dcu_id, userdata)?;
if DCU_GET_REPORT.ioctl(&mut self.0, &mut args)? == 0 {
if let Some(report) = args.extract_report()? {
debug!(
"Get dcu report succeeded - Node: {}, DCU ID: {}",
node, dcu_id
);
report.print_report();
reports.push(report);
}
}
}
}
if reports.is_empty() {
Err(io::Error::new(
io::ErrorKind::NotFound,
"No valid attestation reports obtained from any DCU node",
)
.into())
} else {
Ok(reports)
}
}
}
#[cfg(feature = "network")]
pub async fn verify_reports(
reports: &[AttestationReport],
userdata: &[u8; 64],
) -> Result<(), Error> {
for report in reports {
let cert_data = csv::cert::get_certificate_data(&report.body.chip_id).await?;
verify_report(report, userdata, &cert_data)?;
}
Ok(())
}
pub fn verify_report(
report: &AttestationReport,
userdata: &[u8; 64],
cert_data: &[u8],
) -> Result<(), Error> {
let mut cert_slice = cert_data;
let hsk = ca::Certificate::decode(&mut cert_slice, ())?;
let cek = csv::Certificate::decode(&mut cert_slice, ())?;
let hrk = ca::Certificate::decode(&mut &HRK[..], ())?;
report.print_report();
if userdata != &report.body.user_data {
return Err(
io::Error::new(io::ErrorKind::InvalidData, "Attestation nonce mismatch").into(),
);
}
(&hrk, &hrk).verify()?; (&hrk, &hsk).verify()?; (&hsk, &cek).verify()?; (&cek, report).verify()?;
debug!(
"Successfully verified report for Chip ID: {}",
String::from_utf8_lossy(&report.body.chip_id)
);
Ok(())
}
pub fn save_certificates(
hsk: &ca::Certificate,
cek: &csv::Certificate,
hrk: &ca::Certificate,
chip_id: [u8; 16],
) -> Result<(), Error> {
let certs_dir = Path::new("/opt/dcu/certs");
if !certs_dir.exists() {
fs::create_dir_all(certs_dir).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
}
hsk.write_to_file(&certs_dir.join("hsk.cert"))?;
let chip_id_str = String::from_utf8(chip_id.to_vec())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
cek.write_to_file(&certs_dir.join(format!("{}_cek.cert", chip_id_str)))?;
hrk.write_to_file(&certs_dir.join("hrk.cert"))?;
Ok(())
}