Coverage for /usr/lib/python3/dist-packages/fontTools/ttLib/tables/D_S_I_G_.py: 22%

81 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1from fontTools.misc.textTools import bytesjoin, strjoin, tobytes, tostr, safeEval 

2from fontTools.misc import sstruct 

3from . import DefaultTable 

4import base64 

5 

6DSIG_HeaderFormat = """ 

7 > # big endian 

8 ulVersion: L 

9 usNumSigs: H 

10 usFlag: H 

11""" 

12# followed by an array of usNumSigs DSIG_Signature records 

13DSIG_SignatureFormat = """ 

14 > # big endian 

15 ulFormat: L 

16 ulLength: L # length includes DSIG_SignatureBlock header 

17 ulOffset: L 

18""" 

19# followed by an array of usNumSigs DSIG_SignatureBlock records, 

20# each followed immediately by the pkcs7 bytes 

21DSIG_SignatureBlockFormat = """ 

22 > # big endian 

23 usReserved1: H 

24 usReserved2: H 

25 cbSignature: l # length of following raw pkcs7 data 

26""" 

27 

28# 

29# NOTE 

30# the DSIG table format allows for SignatureBlocks residing 

31# anywhere in the table and possibly in a different order as 

32# listed in the array after the first table header 

33# 

34# this implementation does not keep track of any gaps and/or data 

35# before or after the actual signature blocks while decompiling, 

36# and puts them in the same physical order as listed in the header 

37# on compilation with no padding whatsoever. 

38# 

39 

40 

41class table_D_S_I_G_(DefaultTable.DefaultTable): 

42 def decompile(self, data, ttFont): 

43 dummy, newData = sstruct.unpack2(DSIG_HeaderFormat, data, self) 

44 assert self.ulVersion == 1, "DSIG ulVersion must be 1" 

45 assert self.usFlag & ~1 == 0, "DSIG usFlag must be 0x1 or 0x0" 

46 self.signatureRecords = sigrecs = [] 

47 for n in range(self.usNumSigs): 

48 sigrec, newData = sstruct.unpack2( 

49 DSIG_SignatureFormat, newData, SignatureRecord() 

50 ) 

51 assert sigrec.ulFormat == 1, ( 

52 "DSIG signature record #%d ulFormat must be 1" % n 

53 ) 

54 sigrecs.append(sigrec) 

55 for sigrec in sigrecs: 

56 dummy, newData = sstruct.unpack2( 

57 DSIG_SignatureBlockFormat, data[sigrec.ulOffset :], sigrec 

58 ) 

59 assert sigrec.usReserved1 == 0, ( 

60 "DSIG signature record #%d usReserverd1 must be 0" % n 

61 ) 

62 assert sigrec.usReserved2 == 0, ( 

63 "DSIG signature record #%d usReserverd2 must be 0" % n 

64 ) 

65 sigrec.pkcs7 = newData[: sigrec.cbSignature] 

66 

67 def compile(self, ttFont): 

68 packed = sstruct.pack(DSIG_HeaderFormat, self) 

69 headers = [packed] 

70 offset = len(packed) + self.usNumSigs * sstruct.calcsize(DSIG_SignatureFormat) 

71 data = [] 

72 for sigrec in self.signatureRecords: 

73 # first pack signature block 

74 sigrec.cbSignature = len(sigrec.pkcs7) 

75 packed = sstruct.pack(DSIG_SignatureBlockFormat, sigrec) + sigrec.pkcs7 

76 data.append(packed) 

77 # update redundant length field 

78 sigrec.ulLength = len(packed) 

79 # update running table offset 

80 sigrec.ulOffset = offset 

81 headers.append(sstruct.pack(DSIG_SignatureFormat, sigrec)) 

82 offset += sigrec.ulLength 

83 if offset % 2: 

84 # Pad to even bytes 

85 data.append(b"\0") 

86 return bytesjoin(headers + data) 

87 

88 def toXML(self, xmlWriter, ttFont): 

89 xmlWriter.comment( 

90 "note that the Digital Signature will be invalid after recompilation!" 

91 ) 

92 xmlWriter.newline() 

93 xmlWriter.simpletag( 

94 "tableHeader", 

95 version=self.ulVersion, 

96 numSigs=self.usNumSigs, 

97 flag="0x%X" % self.usFlag, 

98 ) 

99 for sigrec in self.signatureRecords: 

100 xmlWriter.newline() 

101 sigrec.toXML(xmlWriter, ttFont) 

102 xmlWriter.newline() 

103 

104 def fromXML(self, name, attrs, content, ttFont): 

105 if name == "tableHeader": 

106 self.signatureRecords = [] 

107 self.ulVersion = safeEval(attrs["version"]) 

108 self.usNumSigs = safeEval(attrs["numSigs"]) 

109 self.usFlag = safeEval(attrs["flag"]) 

110 return 

111 if name == "SignatureRecord": 

112 sigrec = SignatureRecord() 

113 sigrec.fromXML(name, attrs, content, ttFont) 

114 self.signatureRecords.append(sigrec) 

115 

116 

117pem_spam = lambda l, spam={ 

118 "-----BEGIN PKCS7-----": True, 

119 "-----END PKCS7-----": True, 

120 "": True, 

121}: not spam.get(l.strip()) 

122 

123 

124def b64encode(b): 

125 s = base64.b64encode(b) 

126 # Line-break at 76 chars. 

127 items = [] 

128 while s: 

129 items.append(tostr(s[:76])) 

130 items.append("\n") 

131 s = s[76:] 

132 return strjoin(items) 

133 

134 

135class SignatureRecord(object): 

136 def __repr__(self): 

137 return "<%s: %s>" % (self.__class__.__name__, self.__dict__) 

138 

139 def toXML(self, writer, ttFont): 

140 writer.begintag(self.__class__.__name__, format=self.ulFormat) 

141 writer.newline() 

142 writer.write_noindent("-----BEGIN PKCS7-----\n") 

143 writer.write_noindent(b64encode(self.pkcs7)) 

144 writer.write_noindent("-----END PKCS7-----\n") 

145 writer.endtag(self.__class__.__name__) 

146 

147 def fromXML(self, name, attrs, content, ttFont): 

148 self.ulFormat = safeEval(attrs["format"]) 

149 self.usReserved1 = safeEval(attrs.get("reserved1", "0")) 

150 self.usReserved2 = safeEval(attrs.get("reserved2", "0")) 

151 self.pkcs7 = base64.b64decode(tobytes(strjoin(filter(pem_spam, content))))