use std::path::PathBuf;
use consortium_cfg::Session;
use consortium_cfg_common::config::Party;
use super::{CargoOptions, PipelineError};
use crate::compile::env::CstiWorkspace;
use crate::compile::optee::OpteeConfiguration;
#[derive(Debug, Clone)]
pub struct TeeToolchain {
pub ta_dev_kit_dir: PathBuf,
pub optee_signer: PathBuf,
pub linker: Option<String>,
}
impl TeeToolchain {
pub fn from_env() -> Option<Self> {
let ta_dev_kit_dir = PathBuf::from(std::env::var("TA_DEV_KIT_DIR").ok()?);
let optee_signer = std::env::var("CONSORTIUM_OPTEE_SIGNER")
.map(PathBuf::from)
.unwrap_or_else(|_| ta_dev_kit_dir.join("scripts").join("sign_encrypt.py"));
let linker = std::env::var("CONSORTIUM_TA_LINKER").ok();
Some(Self {
ta_dev_kit_dir,
optee_signer,
linker,
})
}
}
pub fn plan_ta_build(
session: &Session,
workspace: &CstiWorkspace,
toolchain: Option<&TeeToolchain>,
cargo: CargoOptions,
sign: bool,
) -> Result<Option<OpteeConfiguration>, PipelineError> {
let Some(tee) = &session.config.tee else {
return Ok(None);
};
consortium_log::trace!("planning TA build for uuid {}", tee.uuid);
let toolchain = toolchain.ok_or_else(|| {
consortium_log::trace!("TA build requested but no TEE toolchain configured");
PipelineError::MissingTeeToolchain
})?;
let root = session
.config
.endpoints
.get(&Party::AcoreSecure)
.ok_or_else(|| {
consortium_log::trace!("no [endpoints] entry for the secure A-core TA");
PipelineError::MissingEndpoint(Party::AcoreSecure.to_string())
})?;
Ok(Some(OpteeConfiguration {
path: workspace.manifest_dir.join(root),
release: cargo.release,
offline: cargo.offline,
linker: toolchain.linker.clone(),
ta_dev_kit_dir: toolchain.ta_dev_kit_dir.clone(),
optee_signer: toolchain.optee_signer.clone(),
ta_uuid: tee.uuid,
sign,
workspace: Some(workspace.clone()),
}))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pipeline::resolve_workspace;
use std::path::Path;
const STM32: &str = include_str!("../../../consortium-cfg/Consortium.example.stm32mp257.toml");
fn toolchain() -> TeeToolchain {
TeeToolchain {
ta_dev_kit_dir: PathBuf::from("/opt/optee/export-ta_arm64"),
optee_signer: PathBuf::from("/opt/optee/scripts/sign_encrypt.py"),
linker: None,
}
}
#[test]
fn plans_ta_from_manifest_uuid_and_endpoint() {
let session = Session::from_toml(STM32).expect("manifest loads");
let cargo = CargoOptions {
release: true,
offline: true,
};
let cfg = plan_ta_build(
&session,
&resolve_workspace(&session, Path::new("/proj")).unwrap(),
Some(&toolchain()),
cargo,
true,
)
.expect("plan succeeds")
.expect("manifest has [tee]");
assert_eq!(cfg.path, Path::new("/proj/ta"));
assert_eq!(
cfg.ta_uuid.to_string().to_uppercase(),
"48551DD5-74B2-4D24-BF9D-A09F02F5C56F"
);
assert!(cfg.release);
assert!(cfg.offline);
assert!(cfg.sign);
}
#[test]
fn errors_when_toolchain_absent_but_tee_declared() {
let session = Session::from_toml(STM32).expect("manifest loads");
let err = match plan_ta_build(
&session,
&resolve_workspace(&session, Path::new("/proj")).unwrap(),
None,
CargoOptions::default(),
true,
) {
Ok(_) => panic!("no toolchain is an error when [tee] is present"),
Err(err) => err,
};
assert!(matches!(err, PipelineError::MissingTeeToolchain));
}
#[test]
fn returns_none_without_tee_section() {
let manifest = "\
[profile]\n\
chip = \"stm32mp257fak3\"\n\
shared = \"./shared\"\n\
deploy_root = \"/opt/consortium\"\n";
let session = Session::from_toml(manifest).expect("manifest loads");
let plan = plan_ta_build(
&session,
&resolve_workspace(&session, Path::new("/proj")).unwrap(),
Some(&toolchain()),
CargoOptions::default(),
true,
)
.expect("plan succeeds");
assert!(plan.is_none());
}
}