@@ -46,10 +46,12 @@ class Type1Font:
46
46
parts : tuple
47
47
A 3-tuple of the cleartext part, the encrypted part, and the finale of
48
48
zeros.
49
+ decrypted : bytes
50
+ The decrypted form of parts[1].
49
51
prop : dict[str, Any]
50
52
A dictionary of font properties.
51
53
"""
52
- __slots__ = ('parts' , 'prop' )
54
+ __slots__ = ('parts' , 'decrypted' , ' prop' )
53
55
54
56
def __init__ (self , input ):
55
57
"""
@@ -68,6 +70,7 @@ def __init__(self, input):
68
70
data = self ._read (file )
69
71
self .parts = self ._split (data )
70
72
73
+ self .decrypted = self ._decrypt (self .parts [1 ], 'eexec' )
71
74
self ._parse ()
72
75
73
76
def _read (self , file ):
@@ -139,6 +142,57 @@ def _split(self, data):
139
142
_token_re = re .compile (br'/{0,2}[^]\0\t\r\v\n ()<>{}/%[]+' )
140
143
_instring_re = re .compile (br'[()\\]' )
141
144
145
+ @staticmethod
146
+ def _decrypt (ciphertext , key , ndiscard = 4 ):
147
+ """
148
+ Decrypt ciphertext using the Type-1 font algorithm
149
+
150
+ The key argument can be an integer, or one of the strings
151
+ 'eexec' and 'charstring', which map to the key specified for the
152
+ corresponding part of Type-1 fonts.
153
+
154
+ The ndiscard argument should be an integer, usually 4.
155
+ That number of bytes is discarded from the beginning of plaintext.
156
+ """
157
+
158
+ key = {'eexec' : 55665 , 'charstring' : 4330 }.get (key , key )
159
+ if not isinstance (key , int ):
160
+ raise ValueError (f'Invalid decryption key { key !r} ' )
161
+
162
+ plaintext = bytearray (len (ciphertext ))
163
+ for i , byte in enumerate (ciphertext ):
164
+ plaintext [i ] = byte ^ (key >> 8 )
165
+ key = ((key + byte ) * 52845 + 22719 ) & 0xffff
166
+
167
+ return bytes (plaintext [ndiscard :])
168
+
169
+ @staticmethod
170
+ def _encrypt (plaintext , key , ndiscard = 4 ):
171
+ """
172
+ Encrypt plaintext using the Type-1 font algorithm
173
+
174
+ The key argument can be an integer, or one of the strings
175
+ 'eexec' and 'charstring', which map to the key specified for the
176
+ corresponding part of Type-1 fonts.
177
+
178
+ The ndiscard argument should be an integer, usually 4. That
179
+ number of bytes is prepended to the plaintext before encryption.
180
+ This function prepends NUL bytes for reproducibility, even though
181
+ the original algorithm uses random bytes, presumably to avoid
182
+ cryptanalysis.
183
+ """
184
+
185
+ key = {'eexec' : 55665 , 'charstring' : 4330 }.get (key , key )
186
+ if not isinstance (key , int ):
187
+ raise ValueError (f'Invalid encryption key { key !r} ' )
188
+
189
+ ciphertext = bytearray (len (plaintext ) + ndiscard )
190
+ for i , byte in enumerate (b'\0 ' * ndiscard + plaintext ):
191
+ ciphertext [i ] = byte ^ (key >> 8 )
192
+ key = ((key + ciphertext [i ]) * 52845 + 22719 ) & 0xffff
193
+
194
+ return bytes (ciphertext )
195
+
142
196
@classmethod
143
197
def _tokens (cls , text ):
144
198
"""
0 commit comments