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}