1#![deny(missing_docs)]
7
8use chrono::Local;
9use object::{
10 elf::FileHeader64,
11 endian::LittleEndian,
12 read::{elf::ElfFile, pe::PeFile64},
13 Object, ObjectSection, ObjectSymbol,
14};
15use std::{
16 fs::{read, OpenOptions},
17 io::Write,
18 iter::once,
19 num::Wrapping,
20 path::{Path, PathBuf},
21};
22
23pub const MODULE_CAPABILITIES_SYMNAME: &str = "_module_capabilities_";
25pub const MODULE_DATE_SYMNAME: &str = "_module_date";
27pub const TEXT_SECTION_NAME: &str = ".text";
29pub const DATA_SECTION_NAME: &str = ".data";
31pub const MAX_SECTION_CSUM_SIZE: usize = 256;
33pub const SIMICS_SIGNATURE_UNAME_MAX_LEN: usize = 20;
38pub const SIMICS_SIGNATURE_MIN_LENGTH: usize = 44;
40
41type Elf<'data> = ElfFile<'data, FileHeader64<LittleEndian>>;
42
43#[derive(Debug, thiserror::Error)]
44pub enum Error {
46 #[error("File type of {path:?} not recognized. Must be PE64 or ELF64.")]
47 FileTypeNotRecognized {
50 path: PathBuf,
52 },
53 #[error("_module_capabilities_ symbol missing")]
54 ModuleCapabilitiesMissing,
56 #[error("Section not found for symbol {symbol} in {path:?}")]
57 SectionNotFound {
59 symbol: String,
61 path: PathBuf,
63 },
64 #[error("_module_capabilities_ split sequence missing from symbol value")]
65 SplitSequenceNotFound,
67 #[error("Section {section} not found in {path:?}")]
68 SectionMissing {
70 section: String,
72 path: PathBuf,
74 },
75 #[error("Signature unchanged after signing")]
76 SignatureUnchanged,
78 #[error("Module unchanged after signing")]
79 ModuleUnchanged,
81 #[error("File range for section {section} not found")]
82 SectionFileRangeMissing {
84 section: String,
86 },
87 #[error("Original and signed module lengths differ")]
88 ModuleLengthMismatch,
90 #[error("Missing terminating null byte in module capabilities")]
91 NullByteMissing,
93 #[error("Missing parent directory for path {path:?}")]
94 MissingParentDirectory {
96 path: PathBuf,
98 },
99 #[error("Module is not signed.")]
100 ModuleNotSigned,
102 #[error("Failed to open module output file {path:?}: {source}")]
103 OpenOutputFile {
105 path: PathBuf,
107 source: std::io::Error,
109 },
110 #[error("Failed to set permissions for output file {path:?}: {source}")]
111 SetPermissions {
113 path: PathBuf,
115 source: std::io::Error,
117 },
118 #[error("Failed to get metadata for output file {path:?}: {source}")]
119 GetMetadata {
121 path: PathBuf,
123 source: std::io::Error,
125 },
126 #[error("Failed to read directory {path:?}: {source}")]
127 ReadDirectory {
129 path: PathBuf,
131 source: std::io::Error,
133 },
134 #[error("Failed to write module output file to {path:?}: {source}")]
135 WriteOutputFile {
137 path: PathBuf,
139 source: std::io::Error,
141 },
142 #[error(transparent)]
143 IoError(#[from] std::io::Error),
145 #[error(transparent)]
146 ObjectError(#[from] object::Error),
148}
149
150type Result<T> = std::result::Result<T, Error>;
151
152pub struct Sign {
154 module: PathBuf,
155 data: Vec<u8>,
156 signed: Vec<u8>,
157}
158
159impl Sign {
160 pub fn new<P>(module: P) -> Result<Self>
162 where
163 P: AsRef<Path>,
164 {
165 let data = read(module.as_ref())?;
166
167 let mut slf = Self {
168 module: module.as_ref().to_path_buf(),
169 data: data.clone(),
170 signed: Vec::new(),
171 };
172
173 let data = &data[..];
174
175 if let Ok(elf) = Elf::parse(data) {
176 slf.sign_elf(elf)?;
177 Ok(slf)
178 } else if let Ok(pe) = PeFile64::parse(data) {
179 slf.sign_pe(pe)?;
180 Ok(slf)
181 } else {
182 Err(Error::FileTypeNotRecognized {
183 path: slf.module.clone(),
184 })
185 }
186 }
187
188 fn sign_elf(&mut self, elf: Elf) -> Result<()> {
189 let module_capabilities = elf
190 .symbols()
191 .find(|s| s.name() == Ok(MODULE_CAPABILITIES_SYMNAME))
192 .ok_or_else(|| Error::ModuleCapabilitiesMissing)?;
193
194 let module_capabilities_data = &elf.data()[module_capabilities.address() as usize
195 ..module_capabilities.address() as usize + module_capabilities.size() as usize];
196
197 let signature = [b" ".repeat(43), b";\x00".to_vec()].concat();
198
199 let elf_data = elf.data().to_vec();
200
201 if !module_capabilities_data.ends_with(&signature) {
203 println!(
204 "Already signed with signature {:?}",
205 &module_capabilities_data[module_capabilities_data.len() - signature.len()..]
206 );
207 self.signed = elf_data;
208 return Ok(());
210 }
211
212 let split_seq = b"; ";
213
214 let signature_position = module_capabilities_data
215 .windows(split_seq.len())
216 .position(|w| w == split_seq)
217 .ok_or_else(|| Error::SplitSequenceNotFound)?
218 + split_seq.len();
219
220 let text_section =
221 elf.section_by_name(TEXT_SECTION_NAME)
222 .ok_or_else(|| Error::SectionMissing {
223 section: TEXT_SECTION_NAME.to_string(),
224 path: self.module.clone(),
225 })?;
226
227 let data_section =
228 elf.section_by_name(DATA_SECTION_NAME)
229 .ok_or_else(|| Error::SectionMissing {
230 section: DATA_SECTION_NAME.to_string(),
231 path: self.module.clone(),
232 })?;
233
234 let csum: Wrapping<u32> = (Wrapping(1u32)
236 * (Wrapping(text_section.size() as u32)
237 * text_section
238 .data()?
239 .iter()
240 .take(MAX_SECTION_CSUM_SIZE)
241 .fold(Wrapping(0u32), |a, e| a + Wrapping(*e as u32)))
242 * (Wrapping(data_section.size() as u32)
243 * data_section
244 .data()?
245 .iter()
246 .take(MAX_SECTION_CSUM_SIZE)
247 .fold(Wrapping(0u32), |a, e| a + Wrapping(*e as u32))))
248 | Wrapping(1u32);
249
250 let uname = "simics"
251 .chars()
252 .take(SIMICS_SIGNATURE_UNAME_MAX_LEN)
253 .collect::<String>();
254
255 let datetime_string = Local::now().format("%Y-%M-%d %H:%M").to_string();
256
257 let mut signature = module_capabilities_data[..signature_position]
258 .iter()
259 .chain(once(&0u8))
260 .chain(&csum.0.to_le_bytes())
261 .chain(once(&0u8))
262 .chain(datetime_string.as_bytes())
263 .chain(once(&b';'))
264 .chain(uname.as_bytes())
265 .cloned()
266 .collect::<Vec<_>>();
267
268 signature.resize(
269 module_capabilities_data[..signature_position].len() + SIMICS_SIGNATURE_MIN_LENGTH,
270 0u8,
271 );
272
273 if signature == module_capabilities_data {
274 return Err(Error::SignatureUnchanged);
275 }
276
277 let pre_sig = elf_data[..module_capabilities.address() as usize].to_vec();
278
279 let post_sig = elf_data
280 [module_capabilities.address() as usize + module_capabilities.size() as usize..]
281 .to_vec();
282
283 self.signed = pre_sig
284 .iter()
285 .chain(signature.iter())
286 .chain(post_sig.iter())
287 .cloned()
288 .collect::<Vec<_>>();
289
290 if self.data.len() != self.signed.len() {
291 return Err(Error::ModuleLengthMismatch);
292 }
293
294 if self.data == self.signed {
295 return Err(Error::ModuleUnchanged);
296 }
297
298 Ok(())
299 }
300
301 fn sign_pe(&mut self, pe: PeFile64) -> Result<()> {
302 let module_capabilities = pe
303 .symbols()
304 .find(|s| s.name() == Ok(MODULE_CAPABILITIES_SYMNAME))
305 .ok_or_else(|| Error::ModuleCapabilitiesMissing)?;
306
307 let module_capabilities_section =
308 pe.section_by_index(module_capabilities.section().index().ok_or_else(|| {
309 Error::SectionNotFound {
310 symbol: MODULE_CAPABILITIES_SYMNAME.to_string(),
311 path: self.module.clone(),
312 }
313 })?)?;
314 let module_capabilities_offset = ((module_capabilities.address()
315 - module_capabilities_section.address())
316 + module_capabilities_section
317 .file_range()
318 .ok_or_else(|| Error::SectionFileRangeMissing {
319 section: module_capabilities_section
320 .name()
321 .map(|n| n.to_string())
322 .unwrap_or_else(|_| "unknown".to_string()),
323 })?
324 .0) as usize;
325
326 let module_capabilities_size = if module_capabilities.size() > 0 {
327 module_capabilities.size() as usize
328 } else {
329 &pe.data()[module_capabilities_offset..]
330 .iter()
331 .position(|b| *b == 0)
332 .ok_or_else(|| Error::NullByteMissing)?
333 + 1
334 };
335
336 let module_capabilities_data = &pe.data()
337 [module_capabilities_offset..module_capabilities_offset + module_capabilities_size];
338
339 let split_seq = b"; ";
340
341 let signature_position = module_capabilities_data
342 .windows(split_seq.len())
343 .position(|w| w == split_seq)
344 .ok_or_else(|| Error::SplitSequenceNotFound)?
345 + split_seq.len();
346
347 let text_section =
348 pe.section_by_name(TEXT_SECTION_NAME)
349 .ok_or_else(|| Error::SectionMissing {
350 section: TEXT_SECTION_NAME.to_string(),
351 path: self.module.clone(),
352 })?;
353
354 let data_section =
355 pe.section_by_name(DATA_SECTION_NAME)
356 .ok_or_else(|| Error::SectionMissing {
357 section: DATA_SECTION_NAME.to_string(),
358 path: self.module.clone(),
359 })?;
360
361 let csum: Wrapping<u32> = (Wrapping(1u32)
363 * (Wrapping(text_section.size() as u32)
364 * text_section
365 .data()?
366 .iter()
367 .take(MAX_SECTION_CSUM_SIZE)
368 .fold(Wrapping(0u32), |a, e| a + Wrapping(*e as u32)))
369 * (Wrapping(data_section.size() as u32)
370 * data_section
371 .data()?
372 .iter()
373 .take(MAX_SECTION_CSUM_SIZE)
374 .fold(Wrapping(0u32), |a, e| a + Wrapping(*e as u32))))
375 | Wrapping(1u32);
376
377 let uname = "simics"
378 .chars()
379 .take(SIMICS_SIGNATURE_UNAME_MAX_LEN)
380 .collect::<String>();
381
382 let datetime_string = Local::now().format("%Y-%M-%d %H:%M").to_string();
383
384 let mut signature = module_capabilities_data[..signature_position]
385 .iter()
386 .chain(once(&0u8))
387 .chain(&csum.0.to_le_bytes())
388 .chain(once(&0u8))
389 .chain(datetime_string.as_bytes())
390 .chain(once(&b';'))
391 .chain(uname.as_bytes())
392 .cloned()
393 .collect::<Vec<_>>();
394
395 signature.resize(
396 module_capabilities_data[..signature_position].len() + SIMICS_SIGNATURE_MIN_LENGTH,
397 0u8,
398 );
399
400 if signature == module_capabilities_data {
401 return Err(Error::SignatureUnchanged);
402 }
403
404 let pe_data = pe.data().to_vec();
405 let pre_sig = pe_data[..module_capabilities_offset].to_vec();
406 let post_sig = pe_data[module_capabilities_offset + module_capabilities_size..].to_vec();
407 self.signed = pre_sig
408 .iter()
409 .chain(signature.iter())
410 .chain(post_sig.iter())
411 .cloned()
412 .collect::<Vec<_>>();
413
414 if self.data.len() != self.signed.len() {
415 return Err(Error::ModuleLengthMismatch);
416 }
417
418 if self.data == self.signed {
419 return Err(Error::ModuleUnchanged);
420 }
421
422 Ok(())
423 }
424
425 pub fn write_as<S>(&mut self, name: S) -> Result<&mut Self>
427 where
428 S: AsRef<str>,
429 {
430 let output = self
431 .module
432 .parent()
433 .ok_or_else(|| Error::MissingParentDirectory {
434 path: self.module.clone(),
435 })?
436 .join(name.as_ref());
437 let mut file = OpenOptions::new()
438 .create(true)
439 .truncate(true)
440 .write(true)
441 .open(output)?;
442
443 #[cfg(unix)]
444 {
445 use std::os::unix::fs::PermissionsExt;
446 let mut perms = file.metadata()?.permissions();
447 perms.set_mode(0o755);
448 file.set_permissions(perms)?;
449 }
450
451 file.write_all(&self.signed)?;
452 Ok(self)
453 }
454
455 pub fn write<P>(&mut self, output: P) -> Result<&mut Self>
457 where
458 P: AsRef<Path>,
459 {
460 let mut file = OpenOptions::new()
461 .create(true)
462 .truncate(true)
463 .write(true)
464 .open(output.as_ref())
465 .map_err(|e| Error::OpenOutputFile {
466 path: output.as_ref().to_path_buf(),
467 source: e,
468 })?;
469
470 #[cfg(unix)]
471 {
472 use std::os::unix::fs::PermissionsExt;
473 let mut perms = file
474 .metadata()
475 .map_err(|e| Error::GetMetadata {
476 path: output.as_ref().to_path_buf(),
477 source: e,
478 })?
479 .permissions();
480 perms.set_mode(0o755);
481 file.set_permissions(perms)
482 .map_err(|e| Error::SetPermissions {
483 path: output.as_ref().to_path_buf(),
484 source: e,
485 })?;
486 }
487
488 file.write_all(&self.signed)
489 .map_err(|e| Error::WriteOutputFile {
490 path: output.as_ref().to_path_buf(),
491 source: e,
492 })?;
493
494 file.flush()?;
495
496 if !output.as_ref().exists() {
497 return Err(Error::WriteOutputFile {
498 path: output.as_ref().to_path_buf(),
499 source: std::io::Error::from(std::io::ErrorKind::NotFound),
500 });
501 }
502
503 Ok(self)
504 }
505
506 pub fn data(&self) -> Result<Vec<u8>> {
508 if self.signed.is_empty() {
509 Err(Error::ModuleNotSigned)
510 } else {
511 Ok(self.data.clone())
512 }
513 }
514}
515
516#[cfg(test)]
517mod test {
518 use super::Sign;
519 use std::{env::var, path::PathBuf};
520
521 #[cfg(debug_assertions)]
522 const TARGET_DIR: &str = "debug";
523
524 #[cfg(not(debug_assertions))]
525 const TARGET_DIR: &str = "release";
526
527 #[test]
528 #[cfg(windows)]
529 fn test_windows() {
530 let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
531 let workspace_dir = manifest_dir.parent().unwrap();
532 let hello_world = workspace_dir
533 .join("target")
534 .join(TARGET_DIR)
535 .join("hello_world.dll");
536 let _signed = Sign::new(hello_world).unwrap().data().unwrap();
537 }
538
539 #[test]
540 #[cfg(unix)]
541 fn test_linux() {
542 let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
543 let workspace_dir = manifest_dir.parent().unwrap();
544 let hello_world = workspace_dir
545 .join("target")
546 .join(TARGET_DIR)
547 .join("libhello_world.so");
548 println!("{:?}", hello_world);
549 let _signed = Sign::new(hello_world).unwrap().data().unwrap();
550 }
551}