consortium_builder/compile/
optee.rs1use consortium_cfg_common::config::TEE_UUID_ENV;
18use std::path::PathBuf;
19use thiserror::Error;
20use tokio::process::Command;
21use uuid::Uuid;
22
23use super::Compiler;
24use crate::compile::utils::{binary_name, elf_path, resolve_cargo, run_command, verify_elf};
25use crate::progress::BuildReporter;
26
27pub const OPTEE_TARGET: &str = "aarch64-unknown-linux-gnu";
31
32#[derive(Error, Debug)]
34pub enum OpteeCompileError {
35 #[error("TA_DEV_KIT_DIR does not exist: {0:?}")]
36 TaDevKitNotFound(PathBuf),
37
38 #[error("OP-TEE signer not found at: {0:?}")]
39 SignerNotFound(PathBuf),
40
41 #[error("TA signing key not found at: {0:?}")]
42 SigningKeyNotFound(PathBuf),
43
44 #[error("Python interpreter not found on PATH (tried: python, python3)")]
45 PythonNotFound,
46
47 #[error("Cargo build failed: {0}")]
48 BuildFailed(String),
49
50 #[error("ELF not found at expected path: {0:?}")]
51 ElfNotFound(PathBuf),
52
53 #[error("OP-TEE signing failed: {0}")]
54 SigningFailed(String),
55
56 #[error("Signed TA not found after signing: {0:?}")]
57 SignedTaNotFound(PathBuf),
58
59 #[error("{0}")]
60 UtilityError(String),
61}
62
63pub struct OpteeConfiguration {
65 pub path: PathBuf,
67 pub release: bool,
69 pub linker: Option<String>,
71 pub ta_dev_kit_dir: PathBuf,
73 pub optee_signer: PathBuf,
75 pub ta_uuid: Uuid,
77 pub sign: bool,
82}
83
84pub struct OpteeCompiler;
86
87impl OpteeCompiler {
88 pub async fn build_with_reporter(
89 config: OpteeConfiguration,
90 reporter: Option<&dyn BuildReporter>,
91 ) -> Result<PathBuf, OpteeCompileError> {
92 if !config.ta_dev_kit_dir.exists() {
94 return Err(OpteeCompileError::TaDevKitNotFound(config.ta_dev_kit_dir));
95 }
96
97 let cargo = resolve_cargo()
98 .await
99 .map_err(OpteeCompileError::UtilityError)?;
100
101 let bin = binary_name(&config.path)
106 .await
107 .map_err(OpteeCompileError::UtilityError)?;
108
109 let mut cmd = Command::new(&cargo);
110 cmd.arg("build")
111 .arg("--bin")
112 .arg(&bin)
113 .arg("--features")
114 .arg("ta")
115 .arg("--target")
116 .arg(OPTEE_TARGET)
117 .current_dir(&config.path)
118 .env("TA_DEV_KIT_DIR", &config.ta_dev_kit_dir)
119 .env(TEE_UUID_ENV, config.ta_uuid.to_string())
120 .env("RUSTFLAGS", "-C panic=abort");
121
122 if config.release {
123 cmd.arg("--release");
124 }
125
126 if let Some(ref linker) = config.linker {
127 cmd.arg("--config")
128 .arg(format!("{OPTEE_TARGET}.linker={linker}"));
129 }
130
131 let status = run_command(&mut cmd, reporter)
132 .await
133 .map_err(|e| OpteeCompileError::BuildFailed(e.to_string()))?;
134
135 if !status.success() {
136 return Err(OpteeCompileError::BuildFailed(format!(
137 "cargo build failed with status: {status}"
138 )));
139 }
140
141 let elf = Self::output_path(&config).await?;
142
143 if !elf.exists() {
144 return Err(OpteeCompileError::ElfNotFound(elf));
145 }
146
147 verify_elf(&elf)
148 .await
149 .map_err(OpteeCompileError::UtilityError)?;
150
151 if !config.sign {
154 return Ok(elf);
155 }
156
157 if !config.optee_signer.exists() {
159 return Err(OpteeCompileError::SignerNotFound(config.optee_signer));
160 }
161
162 let ta_key = config.ta_dev_kit_dir.join("keys").join("default_ta.pem");
163 if !ta_key.exists() {
164 return Err(OpteeCompileError::SigningKeyNotFound(ta_key));
165 }
166
167 let python = resolve_python().await?;
168
169 let ta = elf
170 .parent()
171 .ok_or_else(|| {
172 OpteeCompileError::UtilityError(format!(
173 "Could not determine output directory for ELF: {elf:?}"
174 ))
175 })?
176 .join(format!("{}.ta", config.ta_uuid));
177
178 let mut sign_cmd = Command::new(&python);
179 sign_cmd
180 .arg(&config.optee_signer)
181 .arg("--uuid")
182 .arg(config.ta_uuid.to_string())
183 .arg("--key")
184 .arg(&ta_key)
185 .arg("--in")
186 .arg(&elf)
187 .arg("--out")
188 .arg(&ta)
189 .env("TA_DEV_KIT_DIR", &config.ta_dev_kit_dir);
190
191 let status = run_command(&mut sign_cmd, reporter).await.map_err(|e| {
192 OpteeCompileError::SigningFailed(format!(
193 "Failed to execute OP-TEE signer via {python}: {e}"
194 ))
195 })?;
196
197 if !status.success() {
198 return Err(OpteeCompileError::SigningFailed(format!(
199 "OP-TEE signing failed with status: {status}"
200 )));
201 }
202
203 if !ta.exists() {
204 return Err(OpteeCompileError::SignedTaNotFound(ta));
205 }
206
207 Ok(ta)
208 }
209}
210
211impl Compiler for OpteeCompiler {
212 type Configuration = OpteeConfiguration;
213 type Error = OpteeCompileError;
214 type Output = PathBuf;
215
216 async fn build(config: Self::Configuration) -> Result<Self::Output, Self::Error> {
217 Self::build_with_reporter(config, None).await
218 }
219
220 async fn output_path(config: &Self::Configuration) -> Result<PathBuf, Self::Error> {
221 elf_path(&config.path, OPTEE_TARGET, config.release)
222 .await
223 .map_err(OpteeCompileError::UtilityError)
224 }
225}
226
227pub struct OpteeBuilder {
229 pub path: PathBuf,
231 pub release: bool,
232 pub linker: Option<String>,
233}
234
235pub struct OpteeEnvironment {
237 pub ta_dev_kit_dir: PathBuf,
238 pub optee_signer: PathBuf,
239 pub ta_uuid: Uuid,
240}
241
242impl OpteeBuilder {
243 pub async fn build(&self, env: &OpteeEnvironment) -> Result<PathBuf, String> {
244 OpteeCompiler::build(OpteeConfiguration {
245 path: self.path.clone(),
246 release: self.release,
247 linker: self.linker.clone(),
248 ta_dev_kit_dir: env.ta_dev_kit_dir.clone(),
249 optee_signer: env.optee_signer.clone(),
250 ta_uuid: env.ta_uuid,
251 sign: true,
252 })
253 .await
254 .map_err(|e| e.to_string())
255 }
256}
257
258async fn resolve_python() -> Result<String, OpteeCompileError> {
259 for candidate in ["python", "python3"] {
260 if let Ok(status) = Command::new(candidate).arg("--version").status().await
261 && status.success()
262 {
263 return Ok(candidate.to_string());
264 }
265 }
266 Err(OpteeCompileError::PythonNotFound)
267}