consortium_builder/compile/
optee.rs1use consortium_cfg_common::config::TEE_UUID_ENV;
32use std::path::PathBuf;
33use thiserror::Error;
34use tokio::process::Command;
35use uuid::Uuid;
36
37use super::Compiler;
38use crate::compile::env::CstiWorkspace;
39use crate::compile::utils::{
40 binary_name, elf_path, inject_csti, resolve_cargo, run_command, verify_elf,
41};
42use crate::progress::BuildReporter;
43
44pub const OPTEE_TARGET: &str = "aarch64-unknown-linux-gnu";
48
49#[derive(Error, Debug)]
51pub enum OpteeCompileError {
52 #[error("TA_DEV_KIT_DIR does not exist: {0:?}")]
53 TaDevKitNotFound(PathBuf),
54
55 #[error("OP-TEE signer not found at: {0:?}")]
56 SignerNotFound(PathBuf),
57
58 #[error("TA signing key not found at: {0:?}")]
59 SigningKeyNotFound(PathBuf),
60
61 #[error("Python interpreter not found on PATH (tried: python, python3)")]
62 PythonNotFound,
63
64 #[error("Cargo failed: {0}")]
65 BuildFailed(String),
66
67 #[error("ELF not found at expected path: {0:?}")]
68 ElfNotFound(PathBuf),
69
70 #[error("OP-TEE signing failed: {0}")]
71 SigningFailed(String),
72
73 #[error("Signed TA not found after signing: {0:?}")]
74 SignedTaNotFound(PathBuf),
75
76 #[error("{0}")]
77 UtilityError(String),
78}
79
80pub struct OpteeConfiguration {
82 pub path: PathBuf,
84 pub release: bool,
86 pub offline: bool,
89 pub linker: Option<String>,
91 pub ta_dev_kit_dir: PathBuf,
93 pub optee_signer: PathBuf,
95 pub ta_uuid: Uuid,
97 pub sign: bool,
102 pub workspace: Option<CstiWorkspace>,
106}
107
108pub struct OpteeCompiler;
110
111impl OpteeCompiler {
112 pub async fn build_with_reporter(
113 config: OpteeConfiguration,
114 reporter: Option<&dyn BuildReporter>,
115 ) -> Result<PathBuf, OpteeCompileError> {
116 Self::cargo(&config, "build", reporter).await?;
118
119 let elf = Self::output_path(&config).await?;
120
121 if !elf.exists() {
122 return Err(OpteeCompileError::ElfNotFound(elf));
123 }
124
125 verify_elf(&elf)
126 .await
127 .map_err(OpteeCompileError::UtilityError)?;
128
129 if !config.sign {
132 return Ok(elf);
133 }
134
135 if !config.optee_signer.exists() {
137 return Err(OpteeCompileError::SignerNotFound(config.optee_signer));
138 }
139
140 let ta_key = config.ta_dev_kit_dir.join("keys").join("default_ta.pem");
141 if !ta_key.exists() {
142 return Err(OpteeCompileError::SigningKeyNotFound(ta_key));
143 }
144
145 let python = resolve_python().await?;
146
147 let ta = elf
148 .parent()
149 .ok_or_else(|| {
150 OpteeCompileError::UtilityError(format!(
151 "Could not determine output directory for ELF: {elf:?}"
152 ))
153 })?
154 .join(format!("{}.ta", config.ta_uuid));
155
156 let mut sign_cmd = Command::new(&python);
157 sign_cmd
158 .arg(&config.optee_signer)
159 .arg("--uuid")
160 .arg(config.ta_uuid.to_string())
161 .arg("--key")
162 .arg(&ta_key)
163 .arg("--in")
164 .arg(&elf)
165 .arg("--out")
166 .arg(&ta)
167 .env("TA_DEV_KIT_DIR", &config.ta_dev_kit_dir);
168
169 let status = run_command(&mut sign_cmd, reporter).await.map_err(|e| {
170 OpteeCompileError::SigningFailed(format!(
171 "Failed to execute OP-TEE signer via {python}: {e}"
172 ))
173 })?;
174
175 if !status.success() {
176 return Err(OpteeCompileError::SigningFailed(format!(
177 "OP-TEE signing failed with status: {status}"
178 )));
179 }
180
181 if !ta.exists() {
182 return Err(OpteeCompileError::SignedTaNotFound(ta));
183 }
184
185 Ok(ta)
186 }
187
188 pub async fn check_with_reporter(
192 config: &OpteeConfiguration,
193 reporter: Option<&dyn BuildReporter>,
194 ) -> Result<(), OpteeCompileError> {
195 Self::cargo(config, "check", reporter).await
196 }
197
198 async fn cargo(
200 config: &OpteeConfiguration,
201 subcommand: &str,
202 reporter: Option<&dyn BuildReporter>,
203 ) -> Result<(), OpteeCompileError> {
204 if !config.ta_dev_kit_dir.exists() {
205 return Err(OpteeCompileError::TaDevKitNotFound(
206 config.ta_dev_kit_dir.clone(),
207 ));
208 }
209
210 let cargo = resolve_cargo()
211 .await
212 .map_err(OpteeCompileError::UtilityError)?;
213
214 let bin = binary_name(&config.path)
219 .await
220 .map_err(OpteeCompileError::UtilityError)?;
221
222 let mut cmd = Command::new(&cargo);
223 cmd.arg(subcommand)
224 .arg("--bin")
225 .arg(&bin)
226 .arg("--features")
227 .arg("ta")
228 .arg("--target")
229 .arg(OPTEE_TARGET)
230 .current_dir(&config.path)
231 .env("TA_DEV_KIT_DIR", &config.ta_dev_kit_dir)
232 .env(TEE_UUID_ENV, config.ta_uuid.to_string())
233 .env("RUSTFLAGS", "-C panic=abort");
234 inject_csti(&mut cmd, config.workspace.as_ref());
235
236 if config.release {
237 cmd.arg("--release");
238 }
239
240 if config.offline {
241 cmd.arg("--offline");
242 }
243
244 if let Some(ref linker) = config.linker {
245 cmd.arg("--config")
246 .arg(format!("{OPTEE_TARGET}.linker={linker}"));
247 }
248
249 let status = run_command(&mut cmd, reporter)
250 .await
251 .map_err(|e| OpteeCompileError::BuildFailed(e.to_string()))?;
252
253 if !status.success() {
254 return Err(OpteeCompileError::BuildFailed(format!(
255 "cargo {subcommand} failed with status: {status}"
256 )));
257 }
258 Ok(())
259 }
260}
261
262impl Compiler for OpteeCompiler {
263 type Configuration = OpteeConfiguration;
264 type Error = OpteeCompileError;
265 type Output = PathBuf;
266
267 async fn build(config: Self::Configuration) -> Result<Self::Output, Self::Error> {
268 Self::build_with_reporter(config, None).await
269 }
270
271 async fn output_path(config: &Self::Configuration) -> Result<PathBuf, Self::Error> {
272 elf_path(
273 &config.path,
274 config.workspace.as_ref(),
275 OPTEE_TARGET,
276 config.release,
277 )
278 .await
279 .map_err(OpteeCompileError::UtilityError)
280 }
281}
282
283pub struct OpteeBuilder {
288 pub path: PathBuf,
290 pub release: bool,
291 pub linker: Option<String>,
292}
293
294pub struct OpteeEnvironment {
296 pub ta_dev_kit_dir: PathBuf,
297 pub optee_signer: PathBuf,
298 pub ta_uuid: Uuid,
299}
300
301impl OpteeBuilder {
302 pub async fn build(&self, env: &OpteeEnvironment) -> Result<PathBuf, String> {
303 OpteeCompiler::build(OpteeConfiguration {
304 path: self.path.clone(),
305 release: self.release,
306 offline: false,
307 linker: self.linker.clone(),
308 ta_dev_kit_dir: env.ta_dev_kit_dir.clone(),
309 optee_signer: env.optee_signer.clone(),
310 ta_uuid: env.ta_uuid,
311 sign: true,
312 workspace: None,
313 })
314 .await
315 .map_err(|e| e.to_string())
316 }
317}
318
319async fn resolve_python() -> Result<String, OpteeCompileError> {
320 for candidate in ["python", "python3"] {
321 if let Ok(status) = Command::new(candidate).arg("--version").status().await
322 && status.success()
323 {
324 return Ok(candidate.to_string());
325 }
326 }
327 Err(OpteeCompileError::PythonNotFound)
328}