eexec.py 3.3 KB

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