D_S_I_G_.py revision 816df48e03957e937ff389acdbccd4889d840491
1from __future__ import print_function, division 2from fontTools.misc.py23 import * 3from fontTools.misc.textTools import safeEval 4from fontTools.misc import sstruct 5from . import DefaultTable 6import base64 7 8DSIG_HeaderFormat = """ 9 > # big endian 10 ulVersion: L 11 usNumSigs: H 12 usFlag: H 13""" 14# followed by an array of usNumSigs DSIG_Signature records 15DSIG_SignatureFormat = """ 16 > # big endian 17 ulFormat: L 18 ulLength: L # length includes DSIG_SignatureBlock header 19 ulOffset: L 20""" 21# followed by an array of usNumSigs DSIG_SignatureBlock records, 22# each followed immediately by the pkcs7 bytes 23DSIG_SignatureBlockFormat = """ 24 > # big endian 25 usReserved1: H 26 usReserved2: H 27 cbSignature: l # length of following raw pkcs7 data 28""" 29 30# 31# NOTE 32# the DSIG table format allows for SignatureBlocks residing 33# anywhere in the table and possibly in a different order as 34# listed in the array after the first table header 35# 36# this implementation does not keep track of any gaps and/or data 37# before or after the actual signature blocks while decompiling, 38# and puts them in the same physical order as listed in the header 39# on compilation with no padding whatsoever. 40# 41 42class table_D_S_I_G_(DefaultTable.DefaultTable): 43 44 def decompile(self, data, ttFont): 45 dummy, newData = sstruct.unpack2(DSIG_HeaderFormat, data, self) 46 assert self.ulVersion == 1, "DSIG ulVersion must be 1" 47 assert self.usFlag & ~1 == 0, "DSIG usFlag must be 0x1 or 0x0" 48 self.signatureRecords = sigrecs = [] 49 for n in range(self.usNumSigs): 50 sigrec, newData = sstruct.unpack2(DSIG_SignatureFormat, newData, SignatureRecord()) 51 assert sigrec.ulFormat == 1, "DSIG signature record #%d ulFormat must be 1" % n 52 sigrecs.append(sigrec) 53 for sigrec in sigrecs: 54 dummy, newData = sstruct.unpack2(DSIG_SignatureBlockFormat, data[sigrec.ulOffset:], sigrec) 55 assert sigrec.usReserved1 == 0, "DSIG signature record #%d usReserverd1 must be 0" % n 56 assert sigrec.usReserved2 == 0, "DSIG signature record #%d usReserverd2 must be 0" % n 57 sigrec.pkcs7 = newData[:sigrec.cbSignature] 58 59 def compile(self, ttFont): 60 packed = sstruct.pack(DSIG_HeaderFormat, self) 61 headers = [packed] 62 offset = len(packed) + self.usNumSigs * sstruct.calcsize(DSIG_SignatureFormat) 63 data = [] 64 for sigrec in self.signatureRecords: 65 # first pack signature block 66 sigrec.cbSignature = len(sigrec.pkcs7) 67 packed = sstruct.pack(DSIG_SignatureBlockFormat, sigrec) + sigrec.pkcs7 68 data.append(packed) 69 # update redundant length field 70 sigrec.ulLength = len(packed) 71 # update running table offset 72 sigrec.ulOffset = offset 73 headers.append(sstruct.pack(DSIG_SignatureFormat, sigrec)) 74 offset += sigrec.ulLength 75 return bytesjoin(headers+data) 76 77 def toXML(self, xmlWriter, ttFont): 78 xmlWriter.comment("note that the Digital Signature will be invalid after recompilation!") 79 xmlWriter.newline() 80 xmlWriter.simpletag("tableHeader", version=self.ulVersion, numSigs=self.usNumSigs, flag="0x%X" % self.usFlag) 81 for sigrec in self.signatureRecords: 82 xmlWriter.newline() 83 sigrec.toXML(xmlWriter, ttFont) 84 xmlWriter.newline() 85 86 def fromXML(self, name, attrs, content, ttFont): 87 if name == "tableHeader": 88 self.signatureRecords = [] 89 self.ulVersion = safeEval(attrs["version"]) 90 self.usNumSigs = safeEval(attrs["numSigs"]) 91 self.usFlag = safeEval(attrs["flag"]) 92 return 93 if name == "SignatureRecord": 94 sigrec = SignatureRecord() 95 sigrec.fromXML(name, attrs, content, ttFont) 96 self.signatureRecords.append(sigrec) 97 98pem_spam = lambda l, spam = { 99 "-----BEGIN PKCS7-----": True, "-----END PKCS7-----": True, "": True 100}: not spam.get(l.strip()) 101 102def b64encode(b): 103 s = base64.b64encode(b) 104 # Line-break at 76 chars. 105 items = [] 106 while s: 107 items.append(tostr(s[:76])) 108 items.append('\n') 109 s = s[76:] 110 return strjoin(items) 111 112class SignatureRecord: 113 def __repr__(self): 114 return "<%s: %s>" % (self.__class__.__name__, self.__dict__) 115 116 def toXML(self, writer, ttFont): 117 writer.begintag(self.__class__.__name__, format=self.ulFormat) 118 writer.newline() 119 writer.write_noindent("-----BEGIN PKCS7-----\n") 120 writer.write_noindent(b64encode(self.pkcs7)) 121 writer.write_noindent("-----END PKCS7-----\n") 122 writer.endtag(self.__class__.__name__) 123 124 def fromXML(self, name, attrs, content, ttFont): 125 self.ulFormat = safeEval(attrs["format"]) 126 self.usReserved1 = safeEval(attrs.get("reserved1", "0")) 127 self.usReserved2 = safeEval(attrs.get("reserved2", "0")) 128 self.pkcs7 = base64.b64decode(tobytes(strjoin(filter(pem_spam, content)))) 129