• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
2PostScript Type 1 fonts make use of two types of encryption: charstring
3encryption and ``eexec`` encryption. Charstring encryption is used for
4the charstrings themselves, while ``eexec`` is used to encrypt larger
5sections of the font program, such as the ``Private`` and ``CharStrings``
6dictionaries. Despite the different names, the algorithm is the same,
7although ``eexec`` encryption uses a fixed initial key R=55665.
8
9The algorithm uses cipher feedback, meaning that the ciphertext is used
10to modify the key. Because of this, the routines in this module return
11the new key at the end of the operation.
12
13"""
14
15from fontTools.misc.textTools import bytechr, bytesjoin, byteord
16
17
18def _decryptChar(cipher, R):
19	cipher = byteord(cipher)
20	plain = ( (cipher ^ (R>>8)) ) & 0xFF
21	R = ( (cipher + R) * 52845 + 22719 ) & 0xFFFF
22	return bytechr(plain), R
23
24def _encryptChar(plain, R):
25	plain = byteord(plain)
26	cipher = ( (plain ^ (R>>8)) ) & 0xFF
27	R = ( (cipher + R) * 52845 + 22719 ) & 0xFFFF
28	return bytechr(cipher), R
29
30
31def decrypt(cipherstring, R):
32	r"""
33	Decrypts a string using the Type 1 encryption algorithm.
34
35	Args:
36		cipherstring: String of ciphertext.
37		R: Initial key.
38
39	Returns:
40		decryptedStr: Plaintext string.
41		R: Output key for subsequent decryptions.
42
43	Examples::
44
45		>>> testStr = b"\0\0asdadads asds\265"
46		>>> decryptedStr, R = decrypt(testStr, 12321)
47		>>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
48		True
49		>>> R == 36142
50		True
51	"""
52	plainList = []
53	for cipher in cipherstring:
54		plain, R = _decryptChar(cipher, R)
55		plainList.append(plain)
56	plainstring = bytesjoin(plainList)
57	return plainstring, int(R)
58
59def encrypt(plainstring, R):
60	r"""
61	Encrypts a string using the Type 1 encryption algorithm.
62
63	Note that the algorithm as described in the Type 1 specification requires the
64	plaintext to be prefixed with a number of random bytes. (For ``eexec`` the
65	number of random bytes is set to 4.) This routine does *not* add the random
66	prefix to its input.
67
68	Args:
69		plainstring: String of plaintext.
70		R: Initial key.
71
72	Returns:
73		cipherstring: Ciphertext string.
74		R: Output key for subsequent encryptions.
75
76	Examples::
77
78		>>> testStr = b"\0\0asdadads asds\265"
79		>>> decryptedStr, R = decrypt(testStr, 12321)
80		>>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
81		True
82		>>> R == 36142
83		True
84
85	>>> testStr = b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
86	>>> encryptedStr, R = encrypt(testStr, 12321)
87	>>> encryptedStr == b"\0\0asdadads asds\265"
88	True
89	>>> R == 36142
90	True
91	"""
92	cipherList = []
93	for plain in plainstring:
94		cipher, R = _encryptChar(plain, R)
95		cipherList.append(cipher)
96	cipherstring = bytesjoin(cipherList)
97	return cipherstring, int(R)
98
99
100def hexString(s):
101	import binascii
102	return binascii.hexlify(s)
103
104def deHexString(h):
105	import binascii
106	h = bytesjoin(h.split())
107	return binascii.unhexlify(h)
108
109
110if __name__ == "__main__":
111	import sys
112	import doctest
113	sys.exit(doctest.testmod().failed)
114