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::utils::{binary_name, elf_path, resolve_cargo, run_command, verify_elf};
39use crate::progress::BuildReporter;
40
41pub const OPTEE_TARGET: &str = "aarch64-unknown-linux-gnu";
45
46#[derive(Error, Debug)]
48pub enum OpteeCompileError {
49 #[error("TA_DEV_KIT_DIR does not exist: {0:?}")]
50 TaDevKitNotFound(PathBuf),
51
52 #[error("OP-TEE signer not found at: {0:?}")]
53 SignerNotFound(PathBuf),
54
55 #[error("TA signing key not found at: {0:?}")]
56 SigningKeyNotFound(PathBuf),
57
58 #[error("Python interpreter not found on PATH (tried: python, python3)")]
59 PythonNotFound,
60
61 #[error("Cargo build failed: {0}")]
62 BuildFailed(String),
63
64 #[error("ELF not found at expected path: {0:?}")]
65 ElfNotFound(PathBuf),
66
67 #[error("OP-TEE signing failed: {0}")]
68 SigningFailed(String),
69
70 #[error("Signed TA not found after signing: {0:?}")]
71 SignedTaNotFound(PathBuf),
72
73 #[error("{0}")]
74 UtilityError(String),
75}
76
77pub struct OpteeConfiguration {
79 pub path: PathBuf,
81 pub release: bool,
83 pub linker: Option<String>,
85 pub ta_dev_kit_dir: PathBuf,
87 pub optee_signer: PathBuf,
89 pub ta_uuid: Uuid,
91 pub sign: bool,
96}
97
98pub struct OpteeCompiler;
100
101impl OpteeCompiler {
102 pub async fn build_with_reporter(
103 config: OpteeConfiguration,
104 reporter: Option<&dyn BuildReporter>,
105 ) -> Result<PathBuf, OpteeCompileError> {
106 if !config.ta_dev_kit_dir.exists() {
108 return Err(OpteeCompileError::TaDevKitNotFound(config.ta_dev_kit_dir));
109 }
110
111 let cargo = resolve_cargo()
112 .await
113 .map_err(OpteeCompileError::UtilityError)?;
114
115 let bin = binary_name(&config.path)
120 .await
121 .map_err(OpteeCompileError::UtilityError)?;
122
123 let mut cmd = Command::new(&cargo);
124 cmd.arg("build")
125 .arg("--bin")
126 .arg(&bin)
127 .arg("--features")
128 .arg("ta")
129 .arg("--target")
130 .arg(OPTEE_TARGET)
131 .current_dir(&config.path)
132 .env("TA_DEV_KIT_DIR", &config.ta_dev_kit_dir)
133 .env(TEE_UUID_ENV, config.ta_uuid.to_string())
134 .env("RUSTFLAGS", "-C panic=abort");
135
136 if config.release {
137 cmd.arg("--release");
138 }
139
140 if let Some(ref linker) = config.linker {
141 cmd.arg("--config")
142 .arg(format!("{OPTEE_TARGET}.linker={linker}"));
143 }
144
145 let status = run_command(&mut cmd, reporter)
146 .await
147 .map_err(|e| OpteeCompileError::BuildFailed(e.to_string()))?;
148
149 if !status.success() {
150 return Err(OpteeCompileError::BuildFailed(format!(
151 "cargo build failed with status: {status}"
152 )));
153 }
154
155 let elf = Self::output_path(&config).await?;
156
157 if !elf.exists() {
158 return Err(OpteeCompileError::ElfNotFound(elf));
159 }
160
161 verify_elf(&elf)
162 .await
163 .map_err(OpteeCompileError::UtilityError)?;
164
165 if !config.sign {
168 return Ok(elf);
169 }
170
171 if !config.optee_signer.exists() {
173 return Err(OpteeCompileError::SignerNotFound(config.optee_signer));
174 }
175
176 let ta_key = config.ta_dev_kit_dir.join("keys").join("default_ta.pem");
177 if !ta_key.exists() {
178 return Err(OpteeCompileError::SigningKeyNotFound(ta_key));
179 }
180
181 let python = resolve_python().await?;
182
183 let ta = elf
184 .parent()
185 .ok_or_else(|| {
186 OpteeCompileError::UtilityError(format!(
187 "Could not determine output directory for ELF: {elf:?}"
188 ))
189 })?
190 .join(format!("{}.ta", config.ta_uuid));
191
192 let mut sign_cmd = Command::new(&python);
193 sign_cmd
194 .arg(&config.optee_signer)
195 .arg("--uuid")
196 .arg(config.ta_uuid.to_string())
197 .arg("--key")
198 .arg(&ta_key)
199 .arg("--in")
200 .arg(&elf)
201 .arg("--out")
202 .arg(&ta)
203 .env("TA_DEV_KIT_DIR", &config.ta_dev_kit_dir);
204
205 let status = run_command(&mut sign_cmd, reporter).await.map_err(|e| {
206 OpteeCompileError::SigningFailed(format!(
207 "Failed to execute OP-TEE signer via {python}: {e}"
208 ))
209 })?;
210
211 if !status.success() {
212 return Err(OpteeCompileError::SigningFailed(format!(
213 "OP-TEE signing failed with status: {status}"
214 )));
215 }
216
217 if !ta.exists() {
218 return Err(OpteeCompileError::SignedTaNotFound(ta));
219 }
220
221 Ok(ta)
222 }
223}
224
225impl Compiler for OpteeCompiler {
226 type Configuration = OpteeConfiguration;
227 type Error = OpteeCompileError;
228 type Output = PathBuf;
229
230 async fn build(config: Self::Configuration) -> Result<Self::Output, Self::Error> {
231 Self::build_with_reporter(config, None).await
232 }
233
234 async fn output_path(config: &Self::Configuration) -> Result<PathBuf, Self::Error> {
235 elf_path(&config.path, OPTEE_TARGET, config.release)
236 .await
237 .map_err(OpteeCompileError::UtilityError)
238 }
239}
240
241pub struct OpteeBuilder {
243 pub path: PathBuf,
245 pub release: bool,
246 pub linker: Option<String>,
247}
248
249pub struct OpteeEnvironment {
251 pub ta_dev_kit_dir: PathBuf,
252 pub optee_signer: PathBuf,
253 pub ta_uuid: Uuid,
254}
255
256impl OpteeBuilder {
257 pub async fn build(&self, env: &OpteeEnvironment) -> Result<PathBuf, String> {
258 OpteeCompiler::build(OpteeConfiguration {
259 path: self.path.clone(),
260 release: self.release,
261 linker: self.linker.clone(),
262 ta_dev_kit_dir: env.ta_dev_kit_dir.clone(),
263 optee_signer: env.optee_signer.clone(),
264 ta_uuid: env.ta_uuid,
265 sign: true,
266 })
267 .await
268 .map_err(|e| e.to_string())
269 }
270}
271
272async fn resolve_python() -> Result<String, OpteeCompileError> {
273 for candidate in ["python", "python3"] {
274 if let Ok(status) = Command::new(candidate).arg("--version").status().await
275 && status.success()
276 {
277 return Ok(candidate.to_string());
278 }
279 }
280 Err(OpteeCompileError::PythonNotFound)
281}