Skip to main content

speck_core/format/
mod.rs

1//! Module format definition and serialization
2//! 
3//! The Speck module format is designed for:
4//! - Compact representation (minimal overhead)
5//! - Streaming parsing (no heap required for verification)
6//! - Position independence (runtime relocatable)
7//! - Extensibility (flags and reserved fields)
8
9use alloc::vec::Vec;
10use alloc::string::String;
11use core::mem::size_of;
12
13use crate::crypto::{KeyPair, PUBLIC_KEY_SIZE, SIGNATURE_SIZE};
14use crate::error::{Error, Result};
15
16pub mod header;
17pub mod manifest;
18
19pub use header::{ModuleHeader, HeaderFlags};
20pub use manifest::ModuleManifest;
21
22/// Magic number for Speck modules: "SPK\x02" (version 2)
23pub const MAGIC: &[u8] = b"SPK\x02";
24
25/// Size of fixed header (everything except code)
26pub const FIXED_HEADER_SIZE: usize = 128;
27
28/// Maximum supported code size (1MB to fit in 20-bit offsets)
29pub const MAX_CODE_SIZE: usize = 1024 * 1024;
30
31/// Flag: Module is signed
32pub const FLAG_SIGNED: u32 = 0x0001;
33
34/// Flag: Code is compressed (gzip)
35pub const FLAG_COMPRESSED: u32 = 0x0002;
36
37/// Flag: Module requires specific hardware revision
38pub const FLAG_HW_LOCKED: u32 = 0x0004;
39
40/// Flag: Critical update (cannot be skipped)
41pub const FLAG_CRITICAL: u32 = 0x0008;
42
43/// Complete module structure
44#[derive(Clone, Debug, PartialEq)]
45pub struct Module {
46    /// Header metadata
47    pub header: ModuleHeader,
48    /// Ed25519 public key (if signed)
49    pub public_key: [u8; PUBLIC_KEY_SIZE],
50    /// Ed25519 signature
51    pub signature: [u8; SIGNATURE_SIZE],
52    /// Code payload
53    pub code: Vec<u8>,
54    /// Optional manifest (JSON/TOML encoded)
55    pub manifest: Option<ModuleManifest>,
56}
57
58impl Module {
59    /// Create a builder for constructing modules
60    pub fn builder() -> ModuleBuilder {
61        ModuleBuilder::default()
62    }
63    
64    /// Serialize to bytes
65    pub fn to_bytes(&self) -> Result<Vec<u8>> {
66        let mut result = Vec::with_capacity(FIXED_HEADER_SIZE + self.code.len());
67        
68        // Magic
69        result.extend_from_slice(MAGIC);
70        
71        // Version
72        result.extend_from_slice(&self.header.version.to_le_bytes());
73        
74        // Total size (header + code)
75        let total_size = FIXED_HEADER_SIZE + self.code.len();
76        result.extend_from_slice(&(total_size as u32).to_le_bytes());
77        
78        // Code size
79        result.extend_from_slice(&(self.code.len() as u32).to_le_bytes());
80        
81        // Entry offset
82        result.extend_from_slice(&self.header.entry_offset.to_le_bytes());
83        
84        // Flags
85        let flags = self.header.flags;
86        result.extend_from_slice(&flags.to_le_bytes());
87        
88        // Monotonic version (anti-rollback)
89        result.extend_from_slice(&self.header.monotonic_version.to_le_bytes());
90        
91        // Hardware revision requirement
92        result.extend_from_slice(&self.header.hw_revision.to_le_bytes());
93        
94        // CRC32 of code (integrity check separate from signature)
95        let crc = crc32fast::hash(&self.code);
96        result.extend_from_slice(&crc.to_le_bytes());
97        
98        // Reserved (8 bytes for future use)
99        result.extend_from_slice(&[0u8; 8]);
100        
101        // Public key
102        result.extend_from_slice(&self.public_key);
103        
104        // Signature
105        result.extend_from_slice(&self.signature);
106        
107        // Code payload
108        result.extend_from_slice(&self.code);
109        
110        Ok(result)
111    }
112    
113    /// Deserialize from bytes
114    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
115        if bytes.len() < FIXED_HEADER_SIZE {
116            return Err(Error::invalid_format("data too short for header"));
117        }
118        
119        // Check magic
120        if &bytes[0..4] != MAGIC {
121            return Err(Error::invalid_format(format!(
122                "invalid magic: expected {:?}, got {:?}", 
123                MAGIC, &bytes[0..4]
124            )));
125        }
126        
127        // Parse version
128        let version = u16::from_le_bytes([bytes[4], bytes[5]]);
129        if version < crate::MIN_MODULE_VERSION {
130            return Err(Error::VersionMismatch {
131                expected: crate::CURRENT_MODULE_VERSION,
132                found: version,
133            });
134        }
135        
136        // Parse sizes
137        let total_size = u32::from_le_bytes([bytes[6], bytes[7], bytes[8], bytes[9]]) as usize;
138        let code_size = u32::from_le_bytes([bytes[10], bytes[11], bytes[12], bytes[13]]) as usize;
139        
140        if code_size > MAX_CODE_SIZE {
141            return Err(Error::invalid_format(format!(
142                "code size {} exceeds maximum {}", code_size, MAX_CODE_SIZE
143            )));
144        }
145        
146        if total_size != FIXED_HEADER_SIZE + code_size {
147            return Err(Error::invalid_format("total size mismatch"));
148        }
149        
150        if bytes.len() < total_size {
151            return Err(Error::invalid_format("truncated module data"));
152        }
153        
154        let header = ModuleHeader {
155            version,
156            entry_offset: u32::from_le_bytes([bytes[14], bytes[15], bytes[16], bytes[17]]),
157            flags: u32::from_le_bytes([bytes[18], bytes[19], bytes[20], bytes[21]]),
158            monotonic_version: u64::from_le_bytes([
159                bytes[22], bytes[23], bytes[24], bytes[25],
160                bytes[26], bytes[27], bytes[28], bytes[29],
161            ]),
162            hw_revision: u32::from_le_bytes([bytes[30], bytes[31], bytes[32], bytes[33]]),
163            code_crc: u32::from_le_bytes([bytes[34], bytes[35], bytes[36], bytes[37]]),
164        };
165        
166        let mut public_key = [0u8; PUBLIC_KEY_SIZE];
167        public_key.copy_from_slice(&bytes[48..80]);
168        
169        let mut signature = [0u8; SIGNATURE_SIZE];
170        signature.copy_from_slice(&bytes[80..144]);
171        
172        let code = bytes[FIXED_HEADER_SIZE..FIXED_HEADER_SIZE + code_size].to_vec();
173        
174        // Verify CRC
175        let actual_crc = crc32fast::hash(&code);
176        if actual_crc != header.code_crc {
177            return Err(Error::invalid_format(format!(
178                "CRC mismatch: expected {}, got {}", header.code_crc, actual_crc
179            )));
180        }
181        
182        Ok(Self {
183            header,
184            public_key,
185            signature,
186            code,
187            manifest: None,
188        })
189    }
190    
191    /// Verify signature using embedded public key
192    pub fn verify(&self) -> Result<()> {
193        KeyPair::verify_module(self)
194    }
195    
196    /// Get the total serialized size
197    pub fn serialized_size(&self) -> usize {
198        FIXED_HEADER_SIZE + self.code.len()
199    }
200    
201    /// Check if module is signed
202    pub fn is_signed(&self) -> bool {
203        self.header.flags & FLAG_SIGNED != 0
204    }
205    
206    /// Check if code is compressed
207    pub fn is_compressed(&self) -> bool {
208        self.header.flags & FLAG_COMPRESSED != 0
209    }
210}
211
212/// Builder for constructing modules
213#[derive(Default)]
214pub struct ModuleBuilder {
215    code: Vec<u8>,
216    entry_offset: u32,
217    flags: u32,
218    monotonic_version: u64,
219    hw_revision: u32,
220    manifest: Option<ModuleManifest>,
221}
222
223impl ModuleBuilder {
224    /// Set the code payload
225    pub fn code(mut self, code: Vec<u8>) -> Self {
226        self.code = code;
227        self
228    }
229    
230    /// Set entry point offset
231    pub fn entry_offset(mut self, offset: u32) -> Self {
232        self.entry_offset = offset;
233        self
234    }
235    
236    /// Set hardware revision requirement
237    pub fn hw_revision(mut self, rev: u32) -> Self {
238        self.hw_revision = rev;
239        self.flags |= FLAG_HW_LOCKED;
240        self
241    }
242    
243    /// Set monotonic version (for anti-rollback)
244    pub fn version(mut self, version: u64) -> Self {
245        self.monotonic_version = version;
246        self
247    }
248    
249    /// Add manifest metadata
250    pub fn manifest(mut self, manifest: ModuleManifest) -> Self {
251        self.manifest = Some(manifest);
252        self
253    }
254    
255    /// Sign the module with a key pair
256    pub fn sign(self, keypair: &KeyPair) -> SignedModuleBuilder {
257        SignedModuleBuilder {
258            inner: self,
259            keypair: keypair.clone(),
260        }
261    }
262    
263    /// Build unsigned module (for testing only)
264    pub fn build_unsigned(self) -> Result<Module> {
265        if self.code.is_empty() {
266            return Err(Error::invalid_format("code cannot be empty"));
267        }
268        
269        Ok(Module {
270            header: ModuleHeader {
271                version: crate::CURRENT_MODULE_VERSION,
272                entry_offset: self.entry_offset,
273                flags: self.flags,
274                monotonic_version: self.monotonic_version,
275                hw_revision: self.hw_revision,
276                code_crc: crc32fast::hash(&self.code),
277            },
278            public_key: [0u8; PUBLIC_KEY_SIZE],
279            signature: [0u8; SIGNATURE_SIZE],
280            code: self.code,
281            manifest: self.manifest,
282        })
283    }
284}
285
286/// Intermediate builder for signed modules
287pub struct SignedModuleBuilder {
288    inner: ModuleBuilder,
289    keypair: KeyPair,
290}
291
292impl SignedModuleBuilder {
293    /// Finalize and build the module
294    pub fn build(self) -> Result<Module> {
295        let mut module = self.inner.build_unsigned()?;
296        self.keypair.sign_module(&mut module);
297        Ok(module)
298    }
299}