read-fonts 0.46.0

Reading OpenType font files.
Documentation
//! The [DSIG](https://learn.microsoft.com/en-us/typography/opentype/spec/dsig) table

include!("../../generated/generated_dsig.rs");

impl SignatureRecord {
    /// The signature block enclosed within this record.
    ///
    /// The `data` argument should be retrieved from the parent table
    /// By calling its `offset_data` method.
    ///
    /// Only format 1 is recognised and read successfully.
    pub fn signature_block<'a>(&self, data: FontData<'a>) -> Option<SignatureBlockFormat1<'a>> {
        if self.format() != 1 {
            return None;
        }
        let signature = self
            .signature_block_offset()
            .resolve::<SignatureBlockFormat1>(data)
            .ok()?;
        // The inner block has to agree with the record's own length field.
        // Where they disagree there is no saying which one to believe, so the
        // block is not readable.
        (self.length() == u32::try_from(signature.compute_len()).ok()?).then_some(signature)
    }
}

impl SignatureBlockFormat1<'_> {
    /// Return the exact byte length of this table.
    fn compute_len(&self) -> usize {
        // This format is contiguous, and includes no trailing data.
        self.min_byte_range().len()
    }
}

#[cfg(test)]
mod tests {
    use font_test_data::bebuffer::BeBuffer;

    use super::{Dsig, PermissionFlags};
    use crate::{FontData, FontRead};

    /// An empty, dummy DSIG, as inserted by fonttools.
    /// See <https://github.com/fonttools/fonttools/blob/ec716f11851f8d5a04e3f535b53219d97001482a/Lib/fontTools/fontBuilder.py#L823-L833>.
    #[test]
    fn test_empty() {
        let buf = BeBuffer::new()
            .push(0x1u32) // version
            .push(0x0u16) // numSignatures
            .push(0x0u16); // flags

        let dsig = Dsig::read(FontData::new(buf.data())).unwrap();
        assert_eq!(dsig.version(), 1);
        assert_eq!(dsig.signature_records().len(), 0);
        assert_eq!(dsig.flags(), PermissionFlags::empty());
    }

    // A DSIG with a single entry. For ease-of-testing, we use 0xDEADBEEF
    // instead of a full PKCS#7 packet, as it would not be this crate's
    // responsibility to validate it anyway.
    #[test]
    fn test_beef() {
        let buf = BeBuffer::new()
            .push(0x1_u32) // DsigHeader.version
            .push(0x1_u16) // DsigHeader.numSignatures
            .push(0x1_u16) // DsigHeader.flags
            .push(0x1_u32) // SignatureRecord.format
            .push(0xC_u32) // SignatureRecord.length
            .push(0x14_u32) // SignatureRecord.signatureBlockOffset
            .push(0x0_u16) // SignatureBlockFormat1.reserved1
            .push(0x0_u16) // SignatureBlockFormat1.reserved2
            .push(0x4_u32) // SignatureBlockFormat1.signatureLength
            .push(0xDEADBEEF_u32); // SignatureBlockFormat1.signature

        let data = FontData::new(buf.data());

        let dsig = Dsig::read(data).unwrap();

        assert_eq!(dsig.version(), 1);
        assert_eq!(dsig.signature_records().len(), 1);
        assert_eq!(dsig.flags(), PermissionFlags::CANNOT_BE_RESIGNED);

        let record = dsig.signature_records()[0];
        assert_eq!(record.format(), 1);

        let block = record.signature_block(data).unwrap();
        assert_eq!(block.signature(), &[0xDE, 0xAD, 0xBE, 0xEF]);
    }

    // A DSIG with a single entry, but in a format we do not recognise; this
    // should fail to read.
    #[test]
    fn test_unknown_format() {
        let buf = BeBuffer::new()
            .push(0x1_u32) // DsigHeader.version
            .push(0x1_u16) // DsigHeader.numSignatures
            .push(0x1_u16) // DsigHeader.flags
            .push(0x2_u32) // SignatureRecord.format, BUT AN UNRECOGNISED ONE
            .push(0xC_u32) // SignatureRecord.length
            .push(0x14_u32) // SignatureRecord.signatureBlockOffset
            .push(0x0_u16) // SignatureBlockFormat1.reserved1
            .push(0x0_u16) // SignatureBlockFormat1.reserved2
            .push(0x4_u32) // SignatureBlockFormat1.signatureLength
            .push(0xDEADBEEF_u32); // SignatureBlockFormat1.signature

        let data = FontData::new(buf.data());

        // Load the first record.
        let dsig = Dsig::read(data).unwrap();

        assert_eq!(dsig.signature_records().len(), 1);
        let record = dsig.signature_records()[0];

        // Assert that it is the unknown format '2', and that it fails to be read.
        assert_eq!(record.format(), 2);

        assert!(record.signature_block(data).is_none());
    }

    // A DSIG with a single entry, whose inner block has a different length to
    // that which the outer record's length field prescribes; this is ambiguous,
    // and so should fail to read.
    #[test]
    fn test_lying_length() {
        let buf = BeBuffer::new()
            .push(0x1_u32) // DsigHeader.version
            .push(0x1_u16) // DsigHeader.numSignatures
            .push(0x1_u16) // DsigHeader.flags
            .push(0x1_u32) // SignatureRecord.format
            .push(0xB_u32) // SignatureRecord.length, BUT ONE LESS THAN THE INNER LENGTH REQUIRES
            .push(0x14_u32) // SignatureRecord.signatureBlockOffset
            .push(0x0_u16) // SignatureBlockFormat1.reserved1
            .push(0x0_u16) // SignatureBlockFormat1.reserved2
            .push(0x4_u32) // SignatureBlockFormat1.signatureLength
            .push(0xDEADBEEF_u32); // SignatureBlockFormat1.signature

        let data = FontData::new(buf.data());

        // Load the first record.
        let dsig = Dsig::read(data).unwrap();

        assert_eq!(dsig.signature_records().len(), 1);
        let record = dsig.signature_records()[0];

        // The inner length requires more bytes than the outer length
        // prescribes, so the block cannot be read.
        assert!(record.signature_block(data).is_none());
    }
}