lucet_module_data/
signature.rs1use crate::error::Error::{self, IOError, ModuleSignatureError};
2use crate::ModuleData;
3use byteorder::{ByteOrder, LittleEndian};
4pub use minisign::{PublicKey, SecretKey};
5use minisign::{SignatureBones, SignatureBox};
6use object::*;
7use std::fs::{File, OpenOptions};
8use std::io::{self, Cursor, Read, Seek, SeekFrom, Write};
9use std::path::Path;
10
11pub struct ModuleSignature;
12
13impl ModuleSignature {
14 pub fn verify<P: AsRef<Path>>(
15 so_path: P,
16 pk: &PublicKey,
17 module_data: &ModuleData,
18 ) -> Result<(), Error> {
19 let signature_box: SignatureBox =
20 SignatureBones::from_bytes(&module_data.get_module_signature())
21 .map_err(|e| ModuleSignatureError(e))?
22 .into();
23
24 let mut raw_module_and_data =
25 RawModuleAndData::from_file(&so_path).map_err(|e| IOError(e))?;
26 let cleared_module_data_bin =
27 ModuleData::clear_module_signature(raw_module_and_data.module_data_bin())?;
28 raw_module_and_data.patch_module_data(&cleared_module_data_bin);
29
30 minisign::verify(
31 &pk,
32 &signature_box,
33 Cursor::new(&raw_module_and_data.obj_bin),
34 true,
35 false,
36 )
37 .map_err(|e| ModuleSignatureError(e))
38 }
39
40 pub fn sign<P: AsRef<Path>>(path: P, sk: &SecretKey) -> Result<(), Error> {
41 let raw_module_and_data = RawModuleAndData::from_file(&path).map_err(|e| IOError(e))?;
42 let signature_box = minisign::sign(
43 None,
44 sk,
45 Cursor::new(&raw_module_and_data.obj_bin),
46 true,
47 None,
48 None,
49 )
50 .map_err(|e| ModuleSignatureError(e))?;
51 let signature_bones: SignatureBones = signature_box.into();
52 let patched_module_data_bin = ModuleData::patch_module_signature(
53 raw_module_and_data.module_data_bin(),
54 &signature_bones.to_bytes(),
55 )?;
56 raw_module_and_data
57 .write_patched_module_data(&path, &patched_module_data_bin)
58 .map_err(|e| IOError(e))?;
59 Ok(())
60 }
61}
62
63struct SymbolData {
64 offset: usize,
65 len: usize,
66}
67
68struct RawModuleAndData {
69 pub obj_bin: Vec<u8>,
70 pub module_data_offset: usize,
71 pub module_data_len: usize,
72}
73
74impl RawModuleAndData {
75 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
76 let mut obj_bin: Vec<u8> = Vec::new();
77 File::open(&path)?.read_to_end(&mut obj_bin)?;
78
79 let module_data_symbol_data = Self::symbol_data(&obj_bin, "lucet_module_data", true)?
80 .ok_or(io::Error::new(
81 io::ErrorKind::InvalidInput,
82 "module data not found",
83 ))?;
84 let module_data_len_symbol_data =
85 Self::symbol_data(&obj_bin, "lucet_module_data_len", true)?.ok_or(io::Error::new(
86 io::ErrorKind::InvalidInput,
87 "module length not found",
88 ))?;
89 assert_eq!(module_data_len_symbol_data.len, 4);
90 assert_eq!(
91 LittleEndian::read_u32(&obj_bin[module_data_len_symbol_data.offset..]) as usize,
92 module_data_symbol_data.len
93 );
94 Ok(RawModuleAndData {
95 obj_bin,
96 module_data_offset: module_data_symbol_data.offset,
97 module_data_len: module_data_symbol_data.len,
98 })
99 }
100
101 pub fn module_data_bin(&self) -> &[u8] {
102 &self.obj_bin[self.module_data_offset as usize
103 ..self.module_data_offset as usize + self.module_data_len]
104 }
105
106 pub fn module_data_bin_mut(&mut self) -> &mut [u8] {
107 &mut self.obj_bin[self.module_data_offset as usize
108 ..self.module_data_offset as usize + self.module_data_len]
109 }
110
111 pub fn patch_module_data(&mut self, module_data_bin: &[u8]) {
112 self.module_data_bin_mut().copy_from_slice(&module_data_bin);
113 }
114
115 pub fn write_patched_module_data<P: AsRef<Path>>(
116 &self,
117 path: P,
118 patched_module_data_bin: &[u8],
119 ) -> Result<(), io::Error> {
120 let mut fp = OpenOptions::new()
121 .write(true)
122 .create_new(false)
123 .open(&path)?;
124 fp.seek(SeekFrom::Start(self.module_data_offset as u64))?;
125 fp.write_all(&patched_module_data_bin)?;
126 Ok(())
127 }
128
129 #[cfg(all(target_family = "unix", not(target_os = "macos")))]
136 fn symbol_data(
137 obj_bin: &[u8],
138 symbol_name: &str,
139 _mangle: bool,
140 ) -> Result<Option<SymbolData>, io::Error> {
141 let obj = object::ElfFile::parse(obj_bin)
142 .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
143 let symbol_map = obj.symbol_map();
144 for symbol in symbol_map.symbols() {
145 let kind = symbol.kind();
146 if kind != SymbolKind::Data {
147 continue;
148 }
149 if symbol.name() != Some(symbol_name) {
150 continue;
151 }
152 let section_index = match symbol.section_index() {
153 Some(section_index) => section_index,
154 None => continue,
155 };
156 let section = &obj.elf().section_headers[section_index.0];
157 let offset = (symbol.address() - section.sh_addr + section.sh_offset) as usize;
158 let len = symbol.size() as usize;
159 return Ok(Some(SymbolData { offset, len }));
160 }
161 Ok(None)
162 }
163
164 #[cfg(target_os = "macos")]
166 fn symbol_data(
167 obj_bin: &[u8],
168 symbol_name: &str,
169 mangle: bool,
170 ) -> Result<Option<SymbolData>, io::Error> {
171 let obj = object::File::parse(obj_bin)
172 .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
173 let symbol_map = obj.symbol_map();
174 let mangled_symbol_name = format!("_{}", symbol_name);
175 let symbol_name = if mangle {
176 &mangled_symbol_name
177 } else {
178 symbol_name
179 };
180 for symbol in symbol_map.symbols() {
181 let kind = symbol.kind();
182 if kind != SymbolKind::Data && kind != SymbolKind::Unknown {
183 continue;
184 }
185 if symbol.name() != Some(symbol_name) {
186 continue;
187 }
188 let offset = symbol.address() as usize;
189 let len = symbol.size() as usize;
190 return Ok(Some(SymbolData { offset, len }));
191 }
192 Ok(None)
193 }
194}