read_fonts/tables/
dsig.rs

1//! The [DSIG](https://learn.microsoft.com/en-us/typography/opentype/spec/dsig) table
2
3include!("../../generated/generated_dsig.rs");
4
5impl SignatureRecord {
6    /// The signature block enclosed within this record.
7    ///
8    /// The `data` argument should be retrieved from the parent table
9    /// By calling its `offset_data` method.
10    ///
11    /// Only format 1 is recognised and read successfully.
12    pub fn signature_block<'a>(
13        &self,
14        data: FontData<'a>,
15    ) -> Result<SignatureBlockFormat1<'a>, ReadError> {
16        match self.format() {
17            1 => {
18                let signature = self
19                    .signature_block_offset()
20                    .resolve::<SignatureBlockFormat1>(data)?;
21
22                // Check that the inner block's size matches our length field.
23                let actual_len =
24                    u32::try_from(signature.compute_len()).map_err(|_| ReadError::OutOfBounds)?;
25
26                if self.length() == actual_len {
27                    Ok(signature)
28                } else {
29                    Err(ReadError::MalformedData("ambiguous DSIG signature length"))
30                }
31            }
32            unknown => Err(ReadError::InvalidFormat(unknown.into())),
33        }
34    }
35}
36
37impl SignatureBlockFormat1<'_> {
38    /// Return the exact byte length of this table.
39    fn compute_len(&self) -> usize {
40        // This format is contiguous, and includes no trailing data.
41        self.min_byte_range().len()
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use font_test_data::bebuffer::BeBuffer;
48
49    use super::{Dsig, PermissionFlags};
50    use crate::{FontData, FontRead, ReadError};
51
52    /// An empty, dummy DSIG, as inserted by fonttools.
53    /// See <https://github.com/fonttools/fonttools/blob/ec716f11851f8d5a04e3f535b53219d97001482a/Lib/fontTools/fontBuilder.py#L823-L833>.
54    #[test]
55    fn test_empty() {
56        let buf = BeBuffer::new()
57            .push(0x1u32) // version
58            .push(0x0u16) // numSignatures
59            .push(0x0u16); // flags
60
61        let dsig = Dsig::read(FontData::new(buf.data())).unwrap();
62        assert_eq!(dsig.version(), 1);
63        assert_eq!(dsig.signature_records().len(), 0);
64        assert_eq!(dsig.flags(), PermissionFlags::empty());
65    }
66
67    // A DSIG with a single entry. For ease-of-testing, we use 0xDEADBEEF
68    // instead of a full PKCS#7 packet, as it would not be this crate's
69    // responsibility to validate it anyway.
70    #[test]
71    fn test_beef() {
72        let buf = BeBuffer::new()
73            .push(0x1_u32) // DsigHeader.version
74            .push(0x1_u16) // DsigHeader.numSignatures
75            .push(0x1_u16) // DsigHeader.flags
76            .push(0x1_u32) // SignatureRecord.format
77            .push(0xC_u32) // SignatureRecord.length
78            .push(0x14_u32) // SignatureRecord.signatureBlockOffset
79            .push(0x0_u16) // SignatureBlockFormat1.reserved1
80            .push(0x0_u16) // SignatureBlockFormat1.reserved2
81            .push(0x4_u32) // SignatureBlockFormat1.signatureLength
82            .push(0xDEADBEEF_u32); // SignatureBlockFormat1.signature
83
84        let data = FontData::new(buf.data());
85
86        let dsig = Dsig::read(data).unwrap();
87
88        assert_eq!(dsig.version(), 1);
89        assert_eq!(dsig.signature_records().len(), 1);
90        assert_eq!(dsig.flags(), PermissionFlags::CANNOT_BE_RESIGNED);
91
92        let record = dsig.signature_records()[0];
93        assert_eq!(record.format(), 1);
94
95        let block = record.signature_block(data).unwrap();
96        assert_eq!(block.signature(), &[0xDE, 0xAD, 0xBE, 0xEF]);
97    }
98
99    // A DSIG with a single entry, but in a format we do not recognise; this
100    // should fail to read.
101    #[test]
102    fn test_unknown_format() {
103        let buf = BeBuffer::new()
104            .push(0x1_u32) // DsigHeader.version
105            .push(0x1_u16) // DsigHeader.numSignatures
106            .push(0x1_u16) // DsigHeader.flags
107            .push(0x2_u32) // SignatureRecord.format, BUT AN UNRECOGNISED ONE
108            .push(0xC_u32) // SignatureRecord.length
109            .push(0x14_u32) // SignatureRecord.signatureBlockOffset
110            .push(0x0_u16) // SignatureBlockFormat1.reserved1
111            .push(0x0_u16) // SignatureBlockFormat1.reserved2
112            .push(0x4_u32) // SignatureBlockFormat1.signatureLength
113            .push(0xDEADBEEF_u32); // SignatureBlockFormat1.signature
114
115        let data = FontData::new(buf.data());
116
117        // Load the first record.
118        let dsig = Dsig::read(data).unwrap();
119
120        assert_eq!(dsig.signature_records().len(), 1);
121        let record = dsig.signature_records()[0];
122
123        // Assert that it is the unknown format '2', and that it fails to be read.
124        assert_eq!(record.format(), 2);
125
126        let block_attempt = record.signature_block(data);
127        assert_eq!(block_attempt.err(), Some(ReadError::InvalidFormat(2)));
128    }
129
130    // A DSIG with a single entry, whose inner block has a different length to
131    // that which the outer record's length field prescribes; this is ambiguous,
132    // and so should fail to read.
133    #[test]
134    fn test_lying_length() {
135        let buf = BeBuffer::new()
136            .push(0x1_u32) // DsigHeader.version
137            .push(0x1_u16) // DsigHeader.numSignatures
138            .push(0x1_u16) // DsigHeader.flags
139            .push(0x1_u32) // SignatureRecord.format
140            .push(0xB_u32) // SignatureRecord.length, BUT ONE LESS THAN THE INNER LENGTH REQUIRES
141            .push(0x14_u32) // SignatureRecord.signatureBlockOffset
142            .push(0x0_u16) // SignatureBlockFormat1.reserved1
143            .push(0x0_u16) // SignatureBlockFormat1.reserved2
144            .push(0x4_u32) // SignatureBlockFormat1.signatureLength
145            .push(0xDEADBEEF_u32); // SignatureBlockFormat1.signature
146
147        let data = FontData::new(buf.data());
148
149        // Load the first record.
150        let dsig = Dsig::read(data).unwrap();
151
152        assert_eq!(dsig.signature_records().len(), 1);
153        let record = dsig.signature_records()[0];
154
155        // Assert that we yield an error, because the inner length requires more
156        // bytes than the outer length prescribes.
157        let block_attempt = record.signature_block(data);
158        assert_eq!(
159            block_attempt.err(),
160            Some(ReadError::MalformedData("ambiguous DSIG signature length"))
161        );
162    }
163}