lucet_module_data/
signature.rs

1use 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    // Retrieving the offset of a symbol is not supported by the object crate.
130    // In Mach-O, actual file offsets are encoded, whereas Elf encodes virtual
131    // addresses, requiring extra steps to retrieve the section, its base
132    // address as well as the section offset.
133
134    // Elf
135    #[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    // Mach-O
165    #[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}