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