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 24 25def _encryptChar(plain, R): 26 plain = byteord(plain) 27 cipher = ((plain ^ (R >> 8))) & 0xFF 28 R = ((cipher + R) * 52845 + 22719) & 0xFFFF 29 return bytechr(cipher), R 30 31 32def decrypt(cipherstring, R): 33 r""" 34 Decrypts a string using the Type 1 encryption algorithm. 35 36 Args: 37 cipherstring: String of ciphertext. 38 R: Initial key. 39 40 Returns: 41 decryptedStr: Plaintext string. 42 R: Output key for subsequent decryptions. 43 44 Examples:: 45 46 >>> testStr = b"\0\0asdadads asds\265" 47 >>> decryptedStr, R = decrypt(testStr, 12321) 48 >>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1' 49 True 50 >>> R == 36142 51 True 52 """ 53 plainList = [] 54 for cipher in cipherstring: 55 plain, R = _decryptChar(cipher, R) 56 plainList.append(plain) 57 plainstring = bytesjoin(plainList) 58 return plainstring, int(R) 59 60 61def encrypt(plainstring, R): 62 r""" 63 Encrypts a string using the Type 1 encryption algorithm. 64 65 Note that the algorithm as described in the Type 1 specification requires the 66 plaintext to be prefixed with a number of random bytes. (For ``eexec`` the 67 number of random bytes is set to 4.) This routine does *not* add the random 68 prefix to its input. 69 70 Args: 71 plainstring: String of plaintext. 72 R: Initial key. 73 74 Returns: 75 cipherstring: Ciphertext string. 76 R: Output key for subsequent encryptions. 77 78 Examples:: 79 80 >>> testStr = b"\0\0asdadads asds\265" 81 >>> decryptedStr, R = decrypt(testStr, 12321) 82 >>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1' 83 True 84 >>> R == 36142 85 True 86 87 >>> testStr = b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1' 88 >>> encryptedStr, R = encrypt(testStr, 12321) 89 >>> encryptedStr == b"\0\0asdadads asds\265" 90 True 91 >>> R == 36142 92 True 93 """ 94 cipherList = [] 95 for plain in plainstring: 96 cipher, R = _encryptChar(plain, R) 97 cipherList.append(cipher) 98 cipherstring = bytesjoin(cipherList) 99 return cipherstring, int(R) 100 101 102def hexString(s): 103 import binascii 104 105 return binascii.hexlify(s) 106 107 108def deHexString(h): 109 import binascii 110 111 h = bytesjoin(h.split()) 112 return binascii.unhexlify(h) 113 114 115if __name__ == "__main__": 116 import sys 117 import doctest 118 119 sys.exit(doctest.testmod().failed) 120