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