Skip to main content

wsc/signature/
sig_sections.rs

1use log::*;
2use std::io::{BufReader, BufWriter, prelude::*};
3
4use crate::ED25519_PK_ID;
5use crate::SIGNATURE_VERSION;
6use crate::SIGNATURE_WASM_MODULE_CONTENT_TYPE;
7use crate::error::*;
8use crate::wasm_module::*;
9
10pub const SIGNATURE_SECTION_HEADER_NAME: &str = "signature";
11pub const SIGNATURE_SECTION_DELIMITER_NAME: &str = "signature_delimiter";
12
13pub const MAX_HASHES: usize = 64;
14pub const MAX_SIGNATURES: usize = 256;
15/// Maximum certificates in a chain (typically: end-entity, intermediate(s), root)
16pub const MAX_CERTIFICATES: usize = 16;
17
18#[derive(PartialEq, Debug, Clone, Eq)]
19pub struct SignatureForHashes {
20    pub key_id: Option<Vec<u8>>,
21    pub alg_id: u8,
22    pub signature: Vec<u8>,
23    /// Certificate chain for certificate-based signing
24    /// Format: [device_cert_der, intermediate_cert_der, ...]
25    /// Device certificate comes first, root certificate last (optional)
26    pub certificate_chain: Option<Vec<Vec<u8>>>,
27}
28
29#[derive(PartialEq, Debug, Clone, Eq)]
30pub struct SignedHashes {
31    pub hashes: Vec<Vec<u8>>,
32    pub signatures: Vec<SignatureForHashes>,
33}
34
35#[derive(PartialEq, Debug, Clone, Eq)]
36pub struct SignatureData {
37    pub specification_version: u8,
38    pub content_type: u8,
39    pub hash_function: u8,
40    pub signed_hashes_set: Vec<SignedHashes>,
41}
42
43impl SignatureForHashes {
44    pub fn serialize(&self) -> Result<Vec<u8>, WSError> {
45        let mut writer = BufWriter::new(Vec::new());
46        if let Some(key_id) = &self.key_id {
47            varint::put_slice(&mut writer, key_id)?;
48        } else {
49            varint::put(&mut writer, 0)?;
50        }
51        writer.write_all(&[self.alg_id])?;
52        varint::put_slice(&mut writer, &self.signature)?;
53
54        // Serialize certificate chain (optional, for backward compatibility)
55        if let Some(cert_chain) = &self.certificate_chain {
56            varint::put(&mut writer, cert_chain.len() as _)?;
57            for cert in cert_chain {
58                varint::put_slice(&mut writer, cert)?;
59            }
60        } else {
61            varint::put(&mut writer, 0)?; // No certificate chain
62        }
63
64        writer
65            .into_inner()
66            .map_err(|e| WSError::IOError(std::io::Error::other(format!("buffer flush failed: {}", e))))
67    }
68
69    pub fn deserialize(bin: impl AsRef<[u8]>) -> Result<Self, WSError> {
70        let mut reader = BufReader::new(bin.as_ref());
71        let key_id = varint::get_slice(&mut reader)?;
72        let key_id = if key_id.is_empty() {
73            None
74        } else {
75            Some(key_id)
76        };
77        let mut alg_id = [0u8; 1];
78        reader.read_exact(&mut alg_id)?;
79        let alg_id = alg_id[0];
80        if alg_id != ED25519_PK_ID {
81            debug!("Unsupported algorithm: {:02x}", alg_id);
82            return Err(WSError::ParseError);
83        }
84        let signature = varint::get_slice(&mut reader)?;
85
86        // Deserialize certificate chain (optional, for backward compatibility)
87        let certificate_chain = if let Ok(cert_count) = varint::get32(&mut reader) {
88            if cert_count as usize > MAX_CERTIFICATES {
89                debug!(
90                    "Too many certificates: {} (max: {})",
91                    cert_count, MAX_CERTIFICATES
92                );
93                return Err(WSError::TooManyCertificates(MAX_CERTIFICATES));
94            }
95            if cert_count > 0 {
96                let mut certs = Vec::with_capacity(cert_count as usize);
97                for _ in 0..cert_count {
98                    if let Ok(cert) = varint::get_slice(&mut reader) {
99                        certs.push(cert);
100                    }
101                }
102                Some(certs)
103            } else {
104                None
105            }
106        } else {
107            None
108        };
109
110        Ok(Self {
111            key_id,
112            alg_id,
113            signature,
114            certificate_chain,
115        })
116    }
117}
118
119impl SignedHashes {
120    pub fn serialize(&self) -> Result<Vec<u8>, WSError> {
121        let mut writer = BufWriter::new(Vec::new());
122        varint::put(&mut writer, self.hashes.len() as _)?;
123        for hash in &self.hashes {
124            writer.write_all(hash)?;
125        }
126        varint::put(&mut writer, self.signatures.len() as _)?;
127        for signature in &self.signatures {
128            varint::put_slice(&mut writer, &signature.serialize()?)?;
129        }
130        writer
131            .into_inner()
132            .map_err(|e| WSError::IOError(std::io::Error::other(format!("buffer flush failed: {}", e))))
133    }
134
135    pub fn deserialize(bin: impl AsRef<[u8]>) -> Result<Self, WSError> {
136        let mut reader = BufReader::new(bin.as_ref());
137        let hashes_count = varint::get32(&mut reader)? as _;
138        if hashes_count > MAX_HASHES {
139            debug!("Too many hashes: {} (max: {})", hashes_count, MAX_HASHES);
140            return Err(WSError::TooManyHashes(MAX_HASHES));
141        }
142        let mut hashes = Vec::with_capacity(hashes_count);
143        for _ in 0..hashes_count {
144            let mut hash = vec![0; 32];
145            reader.read_exact(&mut hash)?;
146            hashes.push(hash);
147        }
148        let signatures_count = varint::get32(&mut reader)? as _;
149        if signatures_count > MAX_SIGNATURES {
150            debug!(
151                "Too many signatures: {} (max: {})",
152                signatures_count, MAX_SIGNATURES
153            );
154            return Err(WSError::TooManySignatures(MAX_SIGNATURES));
155        }
156        let mut signatures = Vec::with_capacity(signatures_count);
157        for _ in 0..signatures_count {
158            let bin = varint::get_slice(&mut reader)?;
159            if let Ok(signature) = SignatureForHashes::deserialize(bin) {
160                signatures.push(signature);
161            }
162        }
163        Ok(Self { hashes, signatures })
164    }
165}
166
167impl SignatureData {
168    pub fn serialize(&self) -> Result<Vec<u8>, WSError> {
169        let mut writer = BufWriter::new(Vec::new());
170        varint::put(&mut writer, self.specification_version as _)?;
171        varint::put(&mut writer, self.content_type as _)?;
172        varint::put(&mut writer, self.hash_function as _)?;
173        varint::put(&mut writer, self.signed_hashes_set.len() as _)?;
174        for signed_hashes in &self.signed_hashes_set {
175            varint::put_slice(&mut writer, &signed_hashes.serialize()?)?;
176        }
177        writer
178            .into_inner()
179            .map_err(|e| WSError::IOError(std::io::Error::other(format!("buffer flush failed: {}", e))))
180    }
181
182    pub fn deserialize(bin: impl AsRef<[u8]>) -> Result<Self, WSError> {
183        let mut reader = BufReader::new(bin.as_ref());
184        let specification_version = varint::get7(&mut reader)?;
185        if specification_version != SIGNATURE_VERSION {
186            debug!(
187                "Unsupported specification version: {:02x}",
188                specification_version
189            );
190            return Err(WSError::ParseError);
191        }
192        let content_type = varint::get7(&mut reader)?;
193        if content_type != SIGNATURE_WASM_MODULE_CONTENT_TYPE {
194            debug!("Unsupported content type: {:02x}", content_type);
195            return Err(WSError::ParseError);
196        }
197        let hash_function = varint::get7(&mut reader)?;
198        let signed_hashes_count = varint::get32(&mut reader)? as _;
199        if signed_hashes_count > MAX_HASHES {
200            debug!(
201                "Too many hashes: {} (max: {})",
202                signed_hashes_count, MAX_HASHES
203            );
204            return Err(WSError::TooManyHashes(MAX_HASHES));
205        }
206        let mut signed_hashes_set = Vec::with_capacity(signed_hashes_count);
207        for _ in 0..signed_hashes_count {
208            let bin = varint::get_slice(&mut reader)?;
209            let signed_hashes = SignedHashes::deserialize(bin)?;
210            signed_hashes_set.push(signed_hashes);
211        }
212        Ok(Self {
213            specification_version,
214            content_type,
215            hash_function,
216            signed_hashes_set,
217        })
218    }
219}
220
221pub fn new_delimiter_section() -> Result<Section, WSError> {
222    let mut custom_payload = vec![0u8; 16];
223    getrandom::fill(&mut custom_payload)
224        .map_err(|_| WSError::InternalError("RNG error".to_string()))?;
225    Ok(Section::Custom(CustomSection::new(
226        SIGNATURE_SECTION_DELIMITER_NAME.to_string(),
227        custom_payload,
228    )))
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234
235    #[test]
236    fn test_signature_for_hashes_serialize_no_key_id() {
237        let sig = SignatureForHashes {
238            key_id: None,
239            alg_id: ED25519_PK_ID,
240            signature: vec![1, 2, 3, 4],
241            certificate_chain: None,
242        };
243        let serialized = sig.serialize().unwrap();
244        assert!(!serialized.is_empty());
245        // First byte should be 0 (no key_id)
246        assert_eq!(serialized[0], 0);
247    }
248
249    #[test]
250    fn test_signature_for_hashes_serialize_with_key_id() {
251        let sig = SignatureForHashes {
252            key_id: Some(vec![10, 20, 30]),
253            alg_id: ED25519_PK_ID,
254            signature: vec![1, 2, 3, 4],
255            certificate_chain: None,
256        };
257        let serialized = sig.serialize().unwrap();
258        assert!(!serialized.is_empty());
259    }
260
261    #[test]
262    fn test_signature_for_hashes_deserialize() {
263        let original = SignatureForHashes {
264            key_id: Some(vec![5, 6, 7]),
265            alg_id: ED25519_PK_ID,
266            signature: vec![11, 22, 33, 44],
267            certificate_chain: None,
268        };
269        let serialized = original.serialize().unwrap();
270        let deserialized = SignatureForHashes::deserialize(&serialized).unwrap();
271        assert_eq!(deserialized, original);
272    }
273
274    #[test]
275    fn test_signature_for_hashes_roundtrip_no_key_id() {
276        let original = SignatureForHashes {
277            key_id: None,
278            alg_id: ED25519_PK_ID,
279            signature: vec![100, 101, 102],
280            certificate_chain: None,
281        };
282        let serialized = original.serialize().unwrap();
283        let deserialized = SignatureForHashes::deserialize(&serialized).unwrap();
284        assert_eq!(deserialized.key_id, None);
285        assert_eq!(deserialized.alg_id, original.alg_id);
286        assert_eq!(deserialized.signature, original.signature);
287    }
288
289    #[test]
290    fn test_signed_hashes_serialize() {
291        let signed = SignedHashes {
292            hashes: vec![vec![1; 32], vec![2; 32]],
293            signatures: vec![SignatureForHashes {
294                key_id: None,
295                alg_id: ED25519_PK_ID,
296                signature: vec![9, 8, 7],
297                certificate_chain: None,
298            }],
299        };
300        let serialized = signed.serialize().unwrap();
301        assert!(!serialized.is_empty());
302    }
303
304    #[test]
305    fn test_signed_hashes_deserialize() {
306        let original = SignedHashes {
307            hashes: vec![vec![42; 32]],
308            signatures: vec![SignatureForHashes {
309                key_id: Some(vec![1, 2]),
310                alg_id: ED25519_PK_ID,
311                signature: vec![3, 4, 5],
312                certificate_chain: None,
313            }],
314        };
315        let serialized = original.serialize().unwrap();
316        let deserialized = SignedHashes::deserialize(&serialized).unwrap();
317        assert_eq!(deserialized.hashes.len(), 1);
318        assert_eq!(deserialized.hashes[0], vec![42; 32]);
319        assert_eq!(deserialized.signatures.len(), 1);
320    }
321
322    #[test]
323    fn test_signed_hashes_too_many_hashes() {
324        // Create data claiming to have more than MAX_HASHES
325        let mut buf = Vec::new();
326        varint::put(&mut buf, (MAX_HASHES + 1) as u64).unwrap();
327        let result = SignedHashes::deserialize(&buf);
328        assert!(result.is_err());
329        assert!(matches!(result.unwrap_err(), WSError::TooManyHashes(_)));
330    }
331
332    #[test]
333    fn test_signed_hashes_too_many_signatures() {
334        // Create valid hashes section
335        let mut buf = Vec::new();
336        varint::put(&mut buf, 1u64).unwrap(); // 1 hash
337        buf.extend_from_slice(&[0u8; 32]); // The hash
338
339        // Add too many signatures
340        varint::put(&mut buf, (MAX_SIGNATURES + 1) as u64).unwrap();
341
342        let result = SignedHashes::deserialize(&buf);
343        assert!(result.is_err());
344        assert!(matches!(result.unwrap_err(), WSError::TooManySignatures(_)));
345    }
346
347    #[test]
348    fn test_signature_for_hashes_too_many_certificates() {
349        // Create a minimal valid signature, then add too many certificates
350        let mut buf = Vec::new();
351        varint::put(&mut buf, 0u64).unwrap(); // no key_id
352        buf.push(ED25519_PK_ID); // alg_id
353        varint::put_slice(&mut buf, &[1, 2, 3, 4]).unwrap(); // signature
354
355        // Add certificate count that exceeds MAX_CERTIFICATES
356        varint::put(&mut buf, (MAX_CERTIFICATES + 1) as u64).unwrap();
357
358        let result = SignatureForHashes::deserialize(&buf);
359        assert!(result.is_err());
360        assert!(matches!(
361            result.unwrap_err(),
362            WSError::TooManyCertificates(_)
363        ));
364    }
365
366    #[test]
367    fn test_signature_for_hashes_extreme_certificate_count() {
368        // Regression test for OOM vulnerability: crafted input claiming billions of certs
369        let mut buf = Vec::new();
370        varint::put(&mut buf, 0u64).unwrap(); // no key_id
371        buf.push(ED25519_PK_ID); // alg_id
372        varint::put_slice(&mut buf, &[1, 2, 3, 4]).unwrap(); // signature
373
374        // Craft a huge certificate count (what the fuzz crash did)
375        varint::put(&mut buf, 0xFFFF_FFFFu64).unwrap(); // ~4 billion certificates
376
377        let result = SignatureForHashes::deserialize(&buf);
378        assert!(result.is_err());
379        assert!(matches!(
380            result.unwrap_err(),
381            WSError::TooManyCertificates(_)
382        ));
383    }
384
385    #[test]
386    fn test_signature_data_serialize() {
387        let data = SignatureData {
388            specification_version: SIGNATURE_VERSION,
389            content_type: SIGNATURE_WASM_MODULE_CONTENT_TYPE,
390            hash_function: 0x01,
391            signed_hashes_set: vec![SignedHashes {
392                hashes: vec![vec![99; 32]],
393                signatures: vec![],
394            }],
395        };
396        let serialized = data.serialize().unwrap();
397        assert!(!serialized.is_empty());
398    }
399
400    #[test]
401    fn test_signature_data_deserialize() {
402        let original = SignatureData {
403            specification_version: SIGNATURE_VERSION,
404            content_type: SIGNATURE_WASM_MODULE_CONTENT_TYPE,
405            hash_function: 0x01,
406            signed_hashes_set: vec![SignedHashes {
407                hashes: vec![vec![55; 32]],
408                signatures: vec![SignatureForHashes {
409                    key_id: None,
410                    alg_id: ED25519_PK_ID,
411                    signature: vec![1, 2, 3],
412                    certificate_chain: None,
413                }],
414            }],
415        };
416        let serialized = original.serialize().unwrap();
417        let deserialized = SignatureData::deserialize(&serialized).unwrap();
418        assert_eq!(
419            deserialized.specification_version,
420            original.specification_version
421        );
422        assert_eq!(deserialized.content_type, original.content_type);
423        assert_eq!(deserialized.hash_function, original.hash_function);
424        assert_eq!(deserialized.signed_hashes_set.len(), 1);
425    }
426
427    #[test]
428    fn test_signature_data_roundtrip() {
429        let original = SignatureData {
430            specification_version: SIGNATURE_VERSION,
431            content_type: SIGNATURE_WASM_MODULE_CONTENT_TYPE,
432            hash_function: 0x02,
433            signed_hashes_set: vec![
434                SignedHashes {
435                    hashes: vec![vec![1; 32], vec![2; 32]],
436                    signatures: vec![SignatureForHashes {
437                        key_id: Some(vec![10, 11]),
438                        alg_id: ED25519_PK_ID,
439                        signature: vec![20, 21, 22],
440                        certificate_chain: None,
441                    }],
442                },
443                SignedHashes {
444                    hashes: vec![vec![3; 32]],
445                    signatures: vec![],
446                },
447            ],
448        };
449
450        let serialized = original.serialize().unwrap();
451        let deserialized = SignatureData::deserialize(&serialized).unwrap();
452
453        assert_eq!(deserialized, original);
454    }
455
456    #[test]
457    fn test_new_delimiter_section() {
458        let section = new_delimiter_section().unwrap();
459        assert!(section.is_signature_delimiter());
460
461        if let Section::Custom(custom) = section {
462            assert_eq!(custom.name(), SIGNATURE_SECTION_DELIMITER_NAME);
463            assert_eq!(custom.payload().len(), 16);
464        } else {
465            panic!("Expected custom section");
466        }
467    }
468
469    #[test]
470    fn test_new_delimiter_sections_are_unique() {
471        let section1 = new_delimiter_section().unwrap();
472        let section2 = new_delimiter_section().unwrap();
473
474        // Payloads should be different (random)
475        assert_ne!(section1.payload(), section2.payload());
476    }
477}